#Test Branch created
if(!require("R.matlab")){
  install.packages("R.matlab")
}
if(!require("readxl")){
  install.packages("readxl")
}
if(!require("dplyr")){
  install.packages("dplyr")
}
if(!require("readxl")){
  install.packages("readxl")
}
if(!require("ggplot2")){
  install.packages("ggplot2")
}
if(!require("caret")){
  install.packages("caret")
}
if(!require("glmnet")){
  install.packages("glmnet")
}
if(!require("WeightedROC")){
  install.packages("WeightedROC")
}
if(!require("gbm")){
  install.packages("gbm")
}
if(!require("DMwR")){
  install.packages("DMwR")
}
if(!require("OpenImageR")){
 install.packages("OpenImageR")
}
if(!require("AUC")){
 install.packages("AUC")
}
if(!require("e1071")){
 install.packages("e1071")
}
if(!require("randomForest")){
 install.packages("randomForest")
}
if(!require("xgboost")){
 install.packages("xgboost")
}
if(!require("tibble")){
 install.packages("tibble")
}
if(!require("ROSE")){
 install.packages("ROSE")
}
if(!require("tidyverse")){
 install.packages("tidyverse")
}
if(!require("caTools")){
  install.packages("caTools")
}
if(!require("prediction")){
  install.packages("prediction")
}
if(!require("pROC")){
  install.packages("pROC")
}

library(R.matlab)
library(readxl)
library(dplyr)
library(ggplot2)
library(caret)
library(glmnet)
library(WeightedROC)
library(gbm)
library(DMwR)
### new libraries
library(OpenImageR)
library(AUC)
library(e1071)
library(randomForest)
library(xgboost)
library(tibble)
library(ROSE)
library(tidyverse)
library(caTools)
library(prediction)
library(pROC)

Step 0 set work directories

set.seed(2020)

Provide directories for training images. Training images and Training fiducial points will be in different subfolders.

train_dir <- "../data/train_set/" # This will be modified for different data sets.
train_image_dir <- paste(train_dir, "images/", sep="")
train_pt_dir <- paste(train_dir,  "points/", sep="")
train_label_path <- paste(train_dir, "label.csv", sep="")

Step 1: set up controls for evaluation experiments.

In this chunk, we have a set of controls for the evaluation experiments.

run.cv <- TRUE # run cross-validation on the training set
sample.reweight <- FALSE # run sample reweighting in model training
K <- 5  # number of CV folds
run.feature.train <- TRUE # process features for training set
run.test <- TRUE # run evaluation on an independent test set
run.feature.test <- TRUE # process features for test set

# gbm
run.gbm <- FALSE # gbm(imroved) is the chosen advanced model
gbm.numtrees <- 1000 #number of trees to use in gbm
run.poly.feature <- TRUE # process poly features
run.add.poly.feature <- TRUE # and poly features to dist matrix

# svm
run.svm <- FALSE # svm is the chosen advanced model
model.selection <- TRUE # perform model selection on svm models
run.svm.test <- TRUE # evaluate performance on the test set

# random forest
run.rf <- FALSE # random forest is the chosen advanced model
run.balanced.data <- TRUE # whether or not balance the data
train.random.forest <- FALSE # train random forest model
test.random.forest <- TRUE # test random forest model
tune.random.forest <- FALSE # tune random forest model

# ridge
run.ridge <- FALSE # ridge is the chosen advanced model
alpha <- 0 # ridge regression
train.ridge <- TRUE # train ridge model

# PCA + LDA
run.pca_lda <- FALSE # PCA + LDA is the chosen adcanced model
run.select_PC <- TRUE #run different PCs
run.lda <- TRUE # run lda on the training set
run.pca_lad.test <- TRUE # evaluate performance on the test set

Using cross-validation or independent test set evaluation, we compare the performance of models with different specifications.

Step 2: import data and train-test split

#train-test split
info <- read.csv(train_label_path)
n <- nrow(info) #get number of rows from csv
n_train <- round(n*(4/5), 0) #use 4/5 amount of data for training
train_idx <- sample(info$Index, n_train, replace = F) #grab indexes used for training
test_idx <- setdiff(info$Index, train_idx) # get indexes not used for training

Fiducial points are stored in matlab format. In this step, we read them and store them in a list.

#function to read fiducial points
#input: index
#output: matrix of fiducial points corresponding to the index
n_files <- length(list.files(train_image_dir,'*jpg'))
readMat.matrix <- function(index){
     return(round(readMat(paste0(train_pt_dir, sprintf("%04d", index), ".mat"))[[1]],0))
}

#load fiducial points
fiducial_pt_list <- lapply(1:n_files, readMat.matrix)
save(fiducial_pt_list, file="../output/fiducial_pt_list.RData")

Step 3: construct features and responses

Figure1

feature.R is the wrapper for all feature engineering functions and options. The function feature( ) have options that correspond to different scenarios for the project and produces an R object that contains features and responses that are required by all the models that are going to be evaluated later.

source("../lib/feature.R")
tm_feature_train <- NA
if(run.feature.train){
  tm_feature_train <- system.time(dat_train<-feature(fiducial_pt_list,train_idx, run.poly.feature, run.add.poly.feature))
  save(dat_train, file="../output/feature_train.RData")
}else{
  load(file="../output/feature_train.RData")
}

tm_feature_test <- NA
if(run.feature.test){
  tm_feature_test <- system.time(dat_test <- feature(fiducial_pt_list, test_idx, run.poly.feature, run.add.poly.feature))
  save(dat_test, file="../output/feature_test.RData")
}else{
  load(file="../output/feature_test.RData")
}

Step 4: train classification models with training features and responses; run test on test images

Call the train model and test model from library.

train.R and test.R are wrappers for all model training steps and classification/prediction steps.

source("../lib/train.R") 
source("../lib/test.R")

Baseline Model

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 1: Improved GBM Model

if (run.gbm){
  if (sample.reweight){
    tm_train <- system.time(fit_train <- train_gbm(dat_train, s=0.1, K=K, n=gbm.numtrees,w = weight_train))
  } else {
    tm_train <- system.time(fit_train <- train_gbm(dat_train, s=0.1, K=K, n=gbm.numtrees,w = NULL))
  }
  
  # plot the performance
  best.iter.oob <- gbm.perf(fit_train,method="OOB")  # returns out-of-bag estimated best number of trees
  print(best.iter.oob)
  best.iter.cv <- gbm.perf(fit_train,method="cv")   # returns K-fold cv estimate of best number of trees
  print(best.iter.cv)
  
  save(fit_train, file="../output/fit_train.RData")
}
if(run.gbm){
  tm_test = NA
  feature_test <- as.matrix(dat_test[, 1:ncol(dat_test)-1])
  if(run.test){
    load(file="../output/fit_train.RData")
    tm_test <- system.time(prob_pred<-test_gbm(fit_train,as.data.frame(feature_test),n=best.iter.cv,pred.type = 'response'))
    
    label_pred <- colnames(prob_pred)[apply(prob_pred, 1, which.max)]
    
  } else {
    tm_test <- system.time({label_pred <- as.integer(test(fit_train, feature_test, pred.type = 'class')); 
                            prob_pred <- test(fit_train, feature_test, pred.type = 'response')})  
  }
}

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 2: Random Forest

if(run.rf){
  # transfer label column from factor to numeric
  dat_train$label <- as.numeric(dat_train$label)
  dat_test$label <- as.numeric(dat_test$label)

  if(run.balanced.data){
    dat_train_balanced_rose<-ROSE(label~., dat_train,seed=2020)$data
    save(dat_train_balanced_rose, file="../output/balanced_train_data_rose.RData")
    dat_test_balanced_rose <- ROSE(label~., dat_test, seed=2020)$data
    save(dat_test_balanced_rose, file = "../output/balanced_test_data_rose.RData")
  } else {
    load(file = "../output/balanced_train_data_rose.RData")
    load(file = "../output/balanced_test_data_rose.RData")
  }
  table(dat_train_balanced_rose$label)
  table(dat_test_balanced_rose$label)
}
if(run.rf){
  source("../lib/random_forest_old_feature.R")
  if(tune.random.forest){
    time.rf.tune <- system.time(rf.tune <- random_forest_tune(dat_train_balanced_rose))
    save(rf.tune, file="../output/rf_tune.RData")
    save(time.rf.tune, file = "../output/rf_tune_time.RData")
  }else{
    load("../output/rf_tune.RData")
    load("../output/rf_tune_time.RData")
  }
  rf.tune
  time.rf.tune[1]
}

mtry = 308 is the best.

if(run.rf){
  source("../lib/random_forest_old_feature.R")
  if(tune.random.forest){
    # Train 500 trees:
    time.rf.train.tree500 <- system.time(random_forest_fit_500_trees <-
                                           random_forest_train_500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_500_trees, file = "../output/rf_train_500_trees.RData")
    save(time.rf.train.tree500, file = "../output/rf_train_500_trees_time.RData")
    cat("500 tree time", time.rf.train.tree500)
    # Train 1000 trees:
    time.rf.train.tree1000 <- system.time(random_forest_fit_1000_trees <-
                                            random_forest_train_1000(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_1000_trees, file = "../output/rf_train_1000_trees.RData")
    save(time.rf.train.tree1000, file = "../output/rf_train_1000_trees_time.RData")
    cat("1000 tree time", time.rf.train.tree1000)
    # Train 1500 trees:
    time.rf.train.tree1500 <- system.time(random_forest_fit_1500_trees <-
                                            random_forest_train_1500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_1500_trees, file = "../output/rf_train_1500_trees.RData")
    save(time.rf.train.tree1500, file = "../output/rf_train_1500_trees_time.RData")
    cat("1500 tree time", time.rf.train.tree1500)
    # Train 2000 trees:
    time.rf.train.tree2000 <- system.time(random_forest_fit_2000_trees <-
                                            random_forest_train_2000(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_2000_trees, file = "../output/rf_train_2000_trees.RData")
    save(time.rf.train.tree2000, file = "../output/rf_train_2000_trees_time.RData")
    cat("2000 tree time", time.rf.train.tree2000)
    # Train 2500 trees:
    time.rf.train.tree2500 <- system.time(random_forest_fit_2500_trees <-
                                            random_forest_train_2500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_2500_trees, file = "../output/rf_train_2500_trees.RData")
    save(time.rf.train.tree2500, file = "../output/rf_train_2500_trees_time.RData")
    cat("2500 tree time", time.rf.train.tree2500)

    # Train 10 nodes
    time.rf.train.node10 <- system.time(random_forest_fit_10_nodes <-
                                          random_forest_train_10(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_10_nodes, file = "../output/rf_train_10_nodes.RData")
    save(time.rf.train.node10, file = "../output/rf_train_10_nodes_time.RData")
    cat("10 node time", time.rf.train.node10)
    # Train 15 nodes
    time.rf.train.node15 <- system.time(random_forest_fit_15_nodes <-
                                          random_forest_train_15(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_15_nodes, file = "../output/rf_train_15_nodes.RData")
    save(time.rf.train.node15, file = "../output/rf_train_15_nodes_time.RData")
    cat("15 node time", time.rf.train.node15)
    # Train 20 nodes
    time.rf.train.node20 <- system.time(random_forest_fit_20_nodes <-
                                          random_forest_train_20(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_20_nodes, file = "../output/rf_train_20_nodes.RData")
    save(time.rf.train.node20, file = "../output/rf_train_20_nodes_time.RData")
    cat("20 node time", time.rf.train.node20)
    # Train 25 nodes
    time.rf.train.node25 <- system.time(random_forest_fit_25_nodes <-
                                          random_forest_train_25(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_25_nodes, file = "../output/rf_train_25_nodes.RData")
    save(time.rf.train.node25, file = "../output/rf_train_25_nodes_time.RData")
    cat("25 node time", time.rf.train.node25)
    # Train 30 nodes
    time.rf.train.node30 <- system.time(random_forest_fit_30_nodes <-
                                          random_forest_train_30(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_30_nodes, file = "../output/rf_train_30_nodes.RData")
    save(time.rf.train.node30, file = "../output/rf_train_30_nodes_time.RData")
    cat("30 node time", time.rf.train.node30)
  } else {
    load("../output/rf_train_500_trees.RData")
    load("../output/rf_train_1000_trees.RData")
    load("../output/rf_train_1500_trees.RData")
    load("../output/rf_train_2000_trees.RData")
    load("../output/rf_train_2500_trees.RData")
    load("../output/rf_train_10_nodes.RData")
    load("../output/rf_train_15_nodes.RData")
    load("../output/rf_train_20_nodes.RData")
    load("../output/rf_train_25_nodes.RData")
    load("../output/rf_train_30_nodes.RData")
  }



  #Error rate of each hyperparameter:



  # Evaluate each hyperparameter


  # Predicted value from 500 trees' model:
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_500_trees, dat_test)))
  # Evaluate 500 trees:
  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test_balanced_rose$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 500", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 500", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 500", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 500",rf_AUC_imbalanced,".\n")


  # Evaluation from 1000 trees' model:
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1000_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1000_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 1000", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 1000", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 1000", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 1000",rf_AUC_imbalanced,".\n")

  # 1500 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1500_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 1500", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 1500", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 1500", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 1500",rf_AUC_imbalanced,".\n")

  #2000 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2000_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2000_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 2000", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 2000", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 2000", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 2000",rf_AUC_imbalanced,".\n")

  # 2500 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2500_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 2500", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 2500", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 2500", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 2500",rf_AUC_imbalanced,".\n")

  # 10 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_10_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_10_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 10", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 10", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 10", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 10",rf_AUC_imbalanced,".\n")

  # 15 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_15_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_15_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 15", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 15", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 15", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 15",rf_AUC_imbalanced,".\n")

  # 20 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_20_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_20_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 20", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 20", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 20", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 20",rf_AUC_imbalanced,".\n")

  # 25 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_25_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_25_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 25", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 25", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 25", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 25",rf_AUC_imbalanced,".\n")

  # 30 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_30_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_30_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("Accuracy(balanced) 30", rf_accuracy_balanced*100,"%.\n")
  cat("AUC(balanced) 30", rf_AUC_balanced,".\n")
  cat("Accuracy(imbalanced) 30", rf_accuracy_imbalanced*100,"%.\n")
  cat("AUC(imbalanced) 30",rf_AUC_imbalanced,".\n")
}

15 Nodes and 2500 trees is the best.

if(run.rf){
  if(train.random.forest){
    time.rf.train.final.balanced <- system.time(random_forest_fit_final_balanced <-
                                                  random_forest_train(dat_train_balanced_rose, 
                                                                      mtry = 308,
                                                                      tree_number = 2000, 
                                                                      node_size = 15))
    save(random_forest_fit_final_balanced, file = "../output/rf_train_final_balanced_old_feature.RData")
    save(time.rf.train.final.balanced, file = "../output/rf_train_final_time_balanced_old_feature.RData")
    time.rf.train.final.imbalanced <- system.time(random_forest_fit_final_imbalanced <-
                                                  random_forest_train(dat_train, 
                                                                      mtry = 308, 
                                                                      tree_number = 1000,
                                                                      node_size = 15))
    save(time.rf.train.final.imbalanced, file = "../output/rf_train_final_time_imbalanced_old_feature.RData")
    save(random_forest_fit_final_imbalanced, file = "../output/rf_train_final_imbalanced_old_feature.RData")
  } else {
    load("../output/rf_train_final_balanced_old_feature.RData")
    load("../output/rf_train_final_time_balanced_old_feature.RData")
    load("../output/rf_train_final_time_imbalanced_old_feature.RData")
    load("../output/rf_train_final_imbalanced_old_feature.RData")
  }
}
if(run.rf){
  # Balanced:
  if(test.random.forest){
  time.rf.test.final.balanced <- system.time(
    rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_final_balanced,
                                                                     dat_test_balanced_rose))))
  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test_balanced_rose$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)

  cat("AUC(balanced): ", rf_AUC_balanced,".\n")
  cat("Accuracy(balanced)", rf_accuracy_balanced*100,"%.\n")
  cat("Training time: ", time.rf.train.final.balanced[1], "s.\n")
  cat("Testing time: ", time.rf.test.final.balanced[1], "s.\n")

  # Imbalanced:
  time.rf.test.final.imbalanced <- system.time(
    rf_predicted_imbalanced <-as.numeric(as.vector(random_forest_test(random_forest_fit_final_imbalanced,
                                                                      dat_test))))
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat("AUC(imbalanced): ", rf_AUC_imbalanced,".\n")
  cat("Accuracy(imbalanced)", rf_accuracy_imbalanced*100,"%.\n")
  cat("Training time: ", time.rf.train.final.imbalanced[1], "s.\n")
  cat("Testing time: ", time.rf.test.final.imbalanced[1], "s.\n")
  }
}

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 3: SVM Model

if(run.svm){
  tm_svm_rebalanced_train <- NA
  if(sample.reweight){
    tm_svm_rebalanced_train <- system.time(svm_training_data <- ROSE(label ~ ., data = dat_train)$data)
    save(svm_training_data, file="../output/svm_training_data.RData")
    save(tm_svm_rebalanced_train, file="../output/tm_svm_rebalanced_train.RData")
  } else {
    svm_training_data <- dat_train
    tm_svm_rebalanced_train <- tm_feature_train
  }
} #else {
  #load(file="../output/tm_svm_rebalanced_train.RData")
#}
if(run.svm){
  tm_svm_linear_mod <- NA
  tm_svm_radial_mod <- NA
  
  if(model.selection){
    svm_model_auc <- rep(NA, 2)
    
    ### linear kernel
    if(run.cv){
      #best.linear.cost <- svm_linear_cost_tune(svm_training_data)
      #cat("The best cost for svm model with linear kernel is: ", best.linear.cost$best.parameters$cost)
      tm_svm_linear_mod <- system.time(svm_linear_mod <- svm_linear_train(svm_training_data, 0.01, K))
      save(svm_linear_mod, file="../output/svm_linear_mod.RData")
      save(tm_svm_linear_mod, file="../output/tm_svm_linear_mod.RData")
    } else {
      load(file="../output/svm_linear_mod.RData")
      load(file="../output/tm_svm_linear_mod.RData")
    }
    svm_linear_pred <- svm_test(svm_linear_mod, svm_training_data, TRUE)
    #mean(round(svm_linear_pred == svm_training_data$label))
    svm_linear_accu <- mean(round(svm_linear_pred == svm_training_data$label))
    tpr.fpr_linear <- WeightedROC(as.numeric(svm_linear_pred), svm_training_data$label)
    svm_model_auc[1] <- WeightedAUC(tpr.fpr_linear)
    
    
    ### radial basis kernel
    if(run.cv){
      #best.radial.cost <- svm_radial_cost_tune(svm_training_data)
      #radial_cost = best.radial.cost$best.parameters$cost
      #radial_gamma = best.radial.cost$best.parameters$gamma
      
      tm_svm_radial_mod < system.time(svm_radial_mod <- svm_radial_train(svm_training_data, 1, K))
      save(svm_radial_mod, file="../output/svm_radial_mod.RData")
      save(tm_svm_radial_mod, file="../output/tm_svm_radial_mod.RData")
    } else { 
      load(file="../output/svm_radial_mod.RData")
      load(file="../output/tm_svm_radial_mod.RData")
    }
    svm_radial_pred <- svm_test(svm_radial_mod, svm_training_data, TRUE)
    # evaluate performance
    svm_radial_accu <- mean(round(svm_radial_pred == svm_training_data$label))
    tpr.fpr_default <- WeightedROC(as.numeric(svm_radial_pred), svm_training_data$label)
    svm_model_auc[2] <- WeightedAUC(tpr.fpr_default)
  } else {
    load(file="../output/svm_linear_mod.RData")
    load(file="../output/tm_svm_linear_mod.RData")
    load(file="../output/svm_radial_mod.RData")
    load(file="../output/tm_svm_radial_mod.RData")
  }
  
  
  ### Evaluation on Testing Data
  tm_svm_rebalanced_test <- NA
  if(sample.reweight){
    tm_svm_rebalanced_test <- system.time(svm_testing_data <- ROSE(label ~ ., data = dat_test)$data)
    save(svm_testing_data, file="../output/svm_testing_data.RData")
    save(tm_svm_rebalanced_test, file="../output/tm_svm_rebalanced_test.RData")
  } else {
    svm_testing_data <- dat_test
    tm_svm_rebalanced_test <- tm_feature_test
  }
  
  if(run.svm.test){
    svm_auc <- rep(NA, 2)
    svm_accu <- rep(NA, 2)
    ## linear
    tm_svm_linear_test <- system.time(svm_linear_pred <- svm_test(svm_linear_mod, svm_testing_data))
    svm_accu[1] = mean(round(svm_linear_pred == svm_testing_data$label))
    tpr.fpr.linear <- WeightedROC(as.numeric(svm_linear_pred), svm_testing_data$label)
    svm_auc[1] = WeightedAUC(tpr.fpr.linear)
    ## rbf
    tm_svm_rbf_test <- system.time(svm_rbf_pred <- svm_test(svm_radial_mod, svm_testing_data))
    svm_accu[2] = mean(round(svm_rbf_pred == svm_testing_data$label))
    tpr.fpr.rbf <- WeightedROC(as.numeric(svm_linear_pred), svm_testing_data$label)
    svm_auc[2] = WeightedAUC(tpr.fpr.rbf)
    
    save(tm_svm_radial_mod, file="../output/tm_svm_linear_test.RData")
    
    ## performance
    svm_auc
    svm_accu

    cat("The accuracy of svm model is", svm_accu[2]*100, "%.\n")
    cat("The AUC of svm model is", svm_auc[2], ".\n")
  } else {
    load(file="../output/tm_svm_rebalanced_test.RData")
  }
}# else {
  #load(file="../output/svm_radial_mod.RData")
  #load(file="../output/tm_svm_radial_mod.RData")
#}
if(run.svm){
  #cat("Time for rebalancing training data =", tm_svm_rebalanced_train[1], "s \n")
  #cat("Time for rebalancing testing data =", tm_svm_rebalanced_test[1], "s \n")
  #cat("Time for training svm model =", tm_svm_radial_mod[1], "s \n")
  cat("Time for testing svm model=", tm_svm_rbf_test[1], "s \n")
}

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 4: Ridge Model

if(run.ridge){
  tm_ridge_train <- NA
  if (train.ridge){
    dat_train_rebalanced <- ROSE(label ~ ., data = dat_train, seed=2020)$data
    tm_ridge_train <- system.time(ridge_cv_model<-ridge_train(train_data=dat_train_rebalanced, alpha=alpha, K=K, lambda=lambda))
    save(ridge_cv_model, file="../output/ridge_cv_model.RData")
    save(tm_ridge_train, file="../output/ridge_train_time.RData")
  } else {
    load(file="../output/ridge_cv_model.RData")
    load(file="../output/ridge_train_time.RData")
  }
}
if(run.ridge){
  if (run.cv){
    set.seed(2020)
    feature_train = as.matrix(dat_train[, -6007])
    label_train = as.integer(dat_train$label)
    ridge_model = cv.glmnet(x=feature_train, y=label_train, alpha=alpha, nfolds=K, lambda=lambda)
    opt_lambda = ridge_model$lambda.min
    save(opt_lambda, file="../output/ridge_optimal_lambda.RData")
  } else {
    load(file="../output/ridge_optimal_lambda.RData")
  }
}
if(run.ridge){
  tm_ridge_test = NA
  if(run.test){
    load("../output/ridge_cv_model.RData")
    feature_test <- as.matrix(dat_test[, -6007])
    tm_ridge_test <- system.time(label_pred<-as.integer(ridge_test(model=ridge_cv_model, features=feature_test, pred.type = 'class')))
    save(tm_ridge_test, file="../output/ridge_test_time.RData")
  } else{
    load(file="../output/ridge_test_time.RData")
  }
}
if(run.ridge){
  cat("Time for constructing training features=", tm_feature_train[1], "s \n")
  cat("Time for constructing testing features=", tm_feature_test[1], "s \n")
  cat("Time for training ridge model=", tm_ridge_train[1], "s \n") 
  cat("Time for testing ridge model=", tm_ridge_test[1], "s \n")
}
if(run.ridge){
  load("../output/ridge_cv_model.RData")
  feature_test <- as.matrix(dat_test[, -6007])
  label_pred = as.integer(predict(ridge_cv_model, s=opt_lambda, newx=feature_test, type='class'))
  label_test = as.integer(dat_test$label)
  compare <- cbind (label_test, label_pred)
  ridge_accuracy = mean(apply(compare, 1, min)/apply(compare, 1, max)) 
  cat("The accuracy of the ridge model is", ridge_accuracy*100, "%.\n")
  ridge_AUC = auc(roc(label_pred,label_test))
  cat("The AUC of the ridge model is", ridge_AUC, ".\n")
}

——–THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL. THIS IS TO SEPARATE EACH MODEL.———-

Advanced Model 5: PCA + LDA

if(run.pca_lda){
  if(sample.reweight){
    balanced_train_data <- ROSE(label~.,data = dat_train)$data
    save(balanced_train_data, file="../output/feature_balanced_train.RData")
  } else {
    load(balanced_train_data, file="../output/feature_train.RData")
  }
}

Since there are over 6000 features, we implement the PCA method to reduce dimension according to the covariance matrix. We only retain PCs with large variance.

if(run.pca_lda){
  if(run.pca_lad.test){
    if(sample.reweight){
      balanced_test_data <- ROSE(label~.,data = dat_test)$data
      save(balanced_test_data, file="../output/feature_balanced_test.RData")
    } else {
      load(balanced_test_data, file="../output/feature_balanced_test.RData")
    }
  }
  
  if(run.select_PC){
    #separate the features from label
    dat_train_new <- balanced_train_data[,-dim(balanced_train_data)[2]]
    dat_test_new <- balanced_test_data[,-dim(balanced_test_data)[2]]
    #create a vector contain target number of PCs
    num.pca <- c(10,50,500,1000)
    train_pca <- function(num.pca){
      for(i in 1:length(num.pca)){
        #start time for training the model
        train.model.start = proc.time()
        #run PCA
        pca <- prcomp(dat_train_new)
        #store for each potential PC
        train_pca <- data.frame(pca$x[,1:num.pca[i]], label = balanced_train_data[dim(balanced_train_data)[2]])
        pred_pca <- predict(pca,dat_test_new)
        test_pca <- data.frame(pred_pca[,1:num.pca[i]], label = balanced_test_data[dim(balanced_test_data)[2]])
        #fitting the lda model
        lda_pca <- lda(label ~ ., data = train_pca) 
        #stop time for training the model
        train.model.end = proc.time()
        #start time for testing the model
        test.model.start = proc.time()
        #predict lda model
        lda_pred_pca = predict(lda_pca,test_pca[-dim(test_pca)[2]])
        #end time for testing the model
        test.model.end = proc.time()
        #test accuracy
        test_accuracy=confusionMatrix(lda_pred_pca$class, test_pca$label)$overall[1]
        print(list(l1=train.model.end - train.model.start,
               l2=test.model.end - test.model.start,
               l3=test_accuracy))
      }
    }
  train_pca(num.pca)
  }
}

By comparing the training time, test time and accuracy, we use model with 50 PCs.

if(run.pca_lda){
  train.model.start = proc.time()
  if(run.lda){
    pca_10 <- prcomp(dat_train_new)
    train_pca_10 <- data.frame(pca_10$x[,1:50], label = balanced_train_data[dim(balanced_train_data)[2]])
    pred_pca_10 <- predict(pca_10,dat_test_new)
    test_pca_10 <- data.frame(pred_pca_10[,1:50], label = balanced_test_data[dim(balanced_test_data)[2]])
    save(train_pca_10, file="../output/feature_pca_train.RData")
    save(test_pca_10, file="../output/feature_pca_test.RData")  
  } else {
    load(train_pca_10, file="../output/feature_pca_train.RData")
    load(test_pca_10, file="../output/feature_pca_test.RData")  
  }
  #calculate the training time
  lda_pca_10 <- lda(label ~ ., data = train_pca_10, cv = TRUE)
  train.model.end = proc.time()
}
if(run.pca_lda){
  test.model.start = proc.time()
  pred_train_lda <- predict(lda_pca_10, train_pca_10[-dim(train_pca_10)[2]])
  accu_train_lda <- mean(pred_train_lda$class == train_pca_10$label)
  cat("The trainig accuracy of model: LDA", "is", accu_train_lda*100, "%.\n")
  #calculating the test time
  if(run.test){
    pred_test_lda <- predict(lda_pca_10, test_pca_10)
  }
  test.model.end = proc.time()
  save(pred_test_lda, file="../output/fit_train.RData")
  accu_test_lda <- mean(pred_test_lda$class == test_pca_10$label)
  cat("The accuracy of model: LDA", "is", accu_test_lda*100, "%.\n")
  tpr.fpr <- WeightedROC(as.numeric(pred_test_lda$class), test_pca_10$label)
  lda_auc = WeightedAUC(tpr.fpr)
  cat("The AUC of model: LDA is", lda_auc, ".\n")
}

Prediction performance matters, so does the running times for constructing features and for training the model, especially when the computation resource is limited.

if(run.pca_lda){
  tm_train <- train.model.end - train.model.start
  tm_test <- test.model.end - test.model.start
  cat("Time for constructing training features=", tm_feature_train[1], "s \n")
  cat("Time for constructing testing features=", tm_feature_test[1], "s \n")
  cat("Time for training model=", tm_train[1], "s \n") 
  cat("Time for testing model=", tm_test[1], "s \n")
}

Reference

  • Du, S., Tao, Y., & Martinez, A. M. (2014). Compound facial expressions of emotion. Proceedings of the National Academy of Sciences, 111(15), E1454-E1462.
LS0tCnRpdGxlOiAiUHJvamVjdCAzOiBGYWNpYWwgRXhwcmVzc2lvbiBQcmVkaWN0aXZlIE1vZGVsaW5nIgphdXRob3I6ICJKaW5nYmluIENhbywgQ2h1YW5jaHVhbiBMaXUsIERlbm5pcyBTaHBpdHMsIFlpbmd5YW8gV3UsIFppa3VuIFpodWFuZyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3IgbWVzc2FnZT1GQUxTRX0KI1Rlc3QgQnJhbmNoIGNyZWF0ZWQKaWYoIXJlcXVpcmUoIlIubWF0bGFiIikpewogIGluc3RhbGwucGFja2FnZXMoIlIubWF0bGFiIikKfQppZighcmVxdWlyZSgicmVhZHhsIikpewogIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCn0KaWYoIXJlcXVpcmUoImRwbHlyIikpewogIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKfQppZighcmVxdWlyZSgicmVhZHhsIikpewogIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCn0KaWYoIXJlcXVpcmUoImdncGxvdDIiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCn0KaWYoIXJlcXVpcmUoImNhcmV0IikpewogIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKfQppZighcmVxdWlyZSgiZ2xtbmV0IikpewogIGluc3RhbGwucGFja2FnZXMoImdsbW5ldCIpCn0KaWYoIXJlcXVpcmUoIldlaWdodGVkUk9DIikpewogIGluc3RhbGwucGFja2FnZXMoIldlaWdodGVkUk9DIikKfQppZighcmVxdWlyZSgiZ2JtIikpewogIGluc3RhbGwucGFja2FnZXMoImdibSIpCn0KaWYoIXJlcXVpcmUoIkRNd1IiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiRE13UiIpCn0KaWYoIXJlcXVpcmUoIk9wZW5JbWFnZVIiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJPcGVuSW1hZ2VSIikKfQppZighcmVxdWlyZSgiQVVDIikpewogaW5zdGFsbC5wYWNrYWdlcygiQVVDIikKfQppZighcmVxdWlyZSgiZTEwNzEiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJlMTA3MSIpCn0KaWYoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKXsKIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpCn0KaWYoIXJlcXVpcmUoInhnYm9vc3QiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJ4Z2Jvb3N0IikKfQppZighcmVxdWlyZSgidGliYmxlIikpewogaW5zdGFsbC5wYWNrYWdlcygidGliYmxlIikKfQppZighcmVxdWlyZSgiUk9TRSIpKXsKIGluc3RhbGwucGFja2FnZXMoIlJPU0UiKQp9CmlmKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQp9CmlmKCFyZXF1aXJlKCJjYVRvb2xzIikpewogIGluc3RhbGwucGFja2FnZXMoImNhVG9vbHMiKQp9CmlmKCFyZXF1aXJlKCJwcmVkaWN0aW9uIikpewogIGluc3RhbGwucGFja2FnZXMoInByZWRpY3Rpb24iKQp9CmlmKCFyZXF1aXJlKCJwUk9DIikpewogIGluc3RhbGwucGFja2FnZXMoInBST0MiKQp9CgpsaWJyYXJ5KFIubWF0bGFiKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShXZWlnaHRlZFJPQykKbGlicmFyeShnYm0pCmxpYnJhcnkoRE13UikKIyMjIG5ldyBsaWJyYXJpZXMKbGlicmFyeShPcGVuSW1hZ2VSKQpsaWJyYXJ5KEFVQykKbGlicmFyeShlMTA3MSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoeGdib29zdCkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkoUk9TRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY2FUb29scykKbGlicmFyeShwcmVkaWN0aW9uKQpsaWJyYXJ5KHBST0MpCmBgYAoKCiMjIyBTdGVwIDAgc2V0IHdvcmsgZGlyZWN0b3JpZXMKYGBge3Igd2tkaXIsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDIwMjApCmBgYAoKUHJvdmlkZSBkaXJlY3RvcmllcyBmb3IgdHJhaW5pbmcgaW1hZ2VzLiBUcmFpbmluZyBpbWFnZXMgYW5kIFRyYWluaW5nIGZpZHVjaWFsIHBvaW50cyB3aWxsIGJlIGluIGRpZmZlcmVudCBzdWJmb2xkZXJzLiAKCmBgYHtyfQp0cmFpbl9kaXIgPC0gIi4uL2RhdGEvdHJhaW5fc2V0LyIgIyBUaGlzIHdpbGwgYmUgbW9kaWZpZWQgZm9yIGRpZmZlcmVudCBkYXRhIHNldHMuCnRyYWluX2ltYWdlX2RpciA8LSBwYXN0ZSh0cmFpbl9kaXIsICJpbWFnZXMvIiwgc2VwPSIiKQp0cmFpbl9wdF9kaXIgPC0gcGFzdGUodHJhaW5fZGlyLCAgInBvaW50cy8iLCBzZXA9IiIpCnRyYWluX2xhYmVsX3BhdGggPC0gcGFzdGUodHJhaW5fZGlyLCAibGFiZWwuY3N2Iiwgc2VwPSIiKQpgYGAKCiMjIyBTdGVwIDE6IHNldCB1cCBjb250cm9scyBmb3IgZXZhbHVhdGlvbiBleHBlcmltZW50cy4KCkluIHRoaXMgY2h1bmssIHdlIGhhdmUgYSBzZXQgb2YgY29udHJvbHMgZm9yIHRoZSBldmFsdWF0aW9uIGV4cGVyaW1lbnRzLiAKCisgKFQvRikgY3Jvc3MtdmFsaWRhdGlvbiBvbiB0aGUgdHJhaW5pbmcgc2V0CisgKFQvRikgcmV3ZWlnaHRpbmcgdGhlIHNhbXBsZXMgZm9yIHRyYWluaW5nIHNldCAKKyAobnVtYmVyKSBLLCB0aGUgbnVtYmVyIG9mIENWIGZvbGRzCisgKFQvRikgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdHJhaW5pbmcgc2V0CisgKFQvRikgcnVuIGV2YWx1YXRpb24gb24gYW4gaW5kZXBlbmRlbnQgdGVzdCBzZXQKKyAoVC9GKSBwcm9jZXNzIGZlYXR1cmVzIGZvciB0ZXN0IHNldAoKKyAoVC9GKSBydW4gaW1wcm92ZWQgZ2JtIG1vZGVsCisgKG51bWJlcikgZ2JtLm51bXRyZWVzLCB0aGUgbnVtYmVyIG9mIHRyZWVzIHRvIHVzZSBpbiBHQk0gYmFzZWxpbmUKKyAoVC9GKSByZXR1cm4gcG9seW5vbWlhbCBmZWF0dXJlcyBtYXRyaXggb25seQorIChUL0YpIGFkZCBwb2x5bm9taWFsIGZlYXR1cmVzIHRvIHN0YXJ0ZXIgY29kZSBmZWF0dXJlcyBtYXRyaXgKCisgKFQvRikgcnVuIHN2bSBtb2RlbAorIChUL0YpIHBlcmZvcm0gbW9kZWwgc2VsZWN0aW9uIG92ZXIgYSBsaXN0IG9mIHN2bSBtb2RlbHMKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiB0aGUgdGVzdCBzZXQKCisgKFQvRikgcnVuIHJhbmRvbSBmb3Jlc3QgbW9kZWwKKyAoVC9GKSByZWJhbGFuY2UgdHJhaW5pbmcgc2V0CisgKFQvRikgdHJhaW4gcmFuZG9tIGZvcmVzdCBtb2RlbAorIChUL0YpIHJ1biBldmFsdWF0aW9uIG9uIHRoZSB0ZXN0IHNldAorIChUL0YpIHR1bmUgaHlwZXJwYXJhbWV0ZXJzIGZvciByYW5kb20gZm9yZXN0IG1vZGVsCgorIChUL0YpIHJ1biByaWRnZSBtb2RlbAorICgwLzEpIGFscGhhLCBhbHBoYT0wIGZvciByaWRnZSByZWdyZXNzaW9uLCBhbHBoYT0xIGZvciBsYXNzbyByZWdyZXNzaW9uCisgKFQvRikgdHJhaW4gcmlkZ2UgbW9kZWwKCisgKFQvRikgcnVuIFBDQStMREEgbW9kZWwKKyAoVC9GKSBydW4gZGlmZmVyZW50IHByaW5jaXBhbCBjb21wb25lbnRzCisgKFQvRikgcnVuIExEQSBvbiB0cmFpbmluZyBzZXQKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiB0aGUgdGVzdCBzZXQKCmBgYHtyIGV4cF9zZXR1cH0KcnVuLmN2IDwtIFRSVUUgIyBydW4gY3Jvc3MtdmFsaWRhdGlvbiBvbiB0aGUgdHJhaW5pbmcgc2V0CnNhbXBsZS5yZXdlaWdodCA8LSBGQUxTRSAjIHJ1biBzYW1wbGUgcmV3ZWlnaHRpbmcgaW4gbW9kZWwgdHJhaW5pbmcKSyA8LSA1ICAjIG51bWJlciBvZiBDViBmb2xkcwpydW4uZmVhdHVyZS50cmFpbiA8LSBUUlVFICMgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdHJhaW5pbmcgc2V0CnJ1bi50ZXN0IDwtIFRSVUUgIyBydW4gZXZhbHVhdGlvbiBvbiBhbiBpbmRlcGVuZGVudCB0ZXN0IHNldApydW4uZmVhdHVyZS50ZXN0IDwtIFRSVUUgIyBwcm9jZXNzIGZlYXR1cmVzIGZvciB0ZXN0IHNldAoKIyBnYm0KcnVuLmdibSA8LSBGQUxTRSAjIGdibShpbXJvdmVkKSBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCmdibS5udW10cmVlcyA8LSAxMDAwICNudW1iZXIgb2YgdHJlZXMgdG8gdXNlIGluIGdibQpydW4ucG9seS5mZWF0dXJlIDwtIFRSVUUgIyBwcm9jZXNzIHBvbHkgZmVhdHVyZXMKcnVuLmFkZC5wb2x5LmZlYXR1cmUgPC0gVFJVRSAjIGFuZCBwb2x5IGZlYXR1cmVzIHRvIGRpc3QgbWF0cml4CgojIHN2bQpydW4uc3ZtIDwtIEZBTFNFICMgc3ZtIGlzIHRoZSBjaG9zZW4gYWR2YW5jZWQgbW9kZWwKbW9kZWwuc2VsZWN0aW9uIDwtIFRSVUUgIyBwZXJmb3JtIG1vZGVsIHNlbGVjdGlvbiBvbiBzdm0gbW9kZWxzCnJ1bi5zdm0udGVzdCA8LSBUUlVFICMgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gdGhlIHRlc3Qgc2V0CgojIHJhbmRvbSBmb3Jlc3QKcnVuLnJmIDwtIEZBTFNFICMgcmFuZG9tIGZvcmVzdCBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCnJ1bi5iYWxhbmNlZC5kYXRhIDwtIFRSVUUgIyB3aGV0aGVyIG9yIG5vdCBiYWxhbmNlIHRoZSBkYXRhCnRyYWluLnJhbmRvbS5mb3Jlc3QgPC0gRkFMU0UgIyB0cmFpbiByYW5kb20gZm9yZXN0IG1vZGVsCnRlc3QucmFuZG9tLmZvcmVzdCA8LSBUUlVFICMgdGVzdCByYW5kb20gZm9yZXN0IG1vZGVsCnR1bmUucmFuZG9tLmZvcmVzdCA8LSBGQUxTRSAjIHR1bmUgcmFuZG9tIGZvcmVzdCBtb2RlbAoKIyByaWRnZQpydW4ucmlkZ2UgPC0gRkFMU0UgIyByaWRnZSBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCmFscGhhIDwtIDAgIyByaWRnZSByZWdyZXNzaW9uCnRyYWluLnJpZGdlIDwtIFRSVUUgIyB0cmFpbiByaWRnZSBtb2RlbAoKIyBQQ0EgKyBMREEKcnVuLnBjYV9sZGEgPC0gRkFMU0UgIyBQQ0EgKyBMREEgaXMgdGhlIGNob3NlbiBhZGNhbmNlZCBtb2RlbApydW4uc2VsZWN0X1BDIDwtIFRSVUUgI3J1biBkaWZmZXJlbnQgUENzCnJ1bi5sZGEgPC0gVFJVRSAjIHJ1biBsZGEgb24gdGhlIHRyYWluaW5nIHNldApydW4ucGNhX2xhZC50ZXN0IDwtIFRSVUUgIyBldmFsdWF0ZSBwZXJmb3JtYW5jZSBvbiB0aGUgdGVzdCBzZXQKYGBgCgpVc2luZyBjcm9zcy12YWxpZGF0aW9uIG9yIGluZGVwZW5kZW50IHRlc3Qgc2V0IGV2YWx1YXRpb24sIHdlIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIG1vZGVscyB3aXRoIGRpZmZlcmVudCBzcGVjaWZpY2F0aW9ucy4gCgojIyMgU3RlcCAyOiBpbXBvcnQgZGF0YSBhbmQgdHJhaW4tdGVzdCBzcGxpdCAKYGBge3J9CiN0cmFpbi10ZXN0IHNwbGl0CmluZm8gPC0gcmVhZC5jc3YodHJhaW5fbGFiZWxfcGF0aCkKbiA8LSBucm93KGluZm8pICNnZXQgbnVtYmVyIG9mIHJvd3MgZnJvbSBjc3YKbl90cmFpbiA8LSByb3VuZChuKig0LzUpLCAwKSAjdXNlIDQvNSBhbW91bnQgb2YgZGF0YSBmb3IgdHJhaW5pbmcKdHJhaW5faWR4IDwtIHNhbXBsZShpbmZvJEluZGV4LCBuX3RyYWluLCByZXBsYWNlID0gRikgI2dyYWIgaW5kZXhlcyB1c2VkIGZvciB0cmFpbmluZwp0ZXN0X2lkeCA8LSBzZXRkaWZmKGluZm8kSW5kZXgsIHRyYWluX2lkeCkgIyBnZXQgaW5kZXhlcyBub3QgdXNlZCBmb3IgdHJhaW5pbmcKYGBgCgpGaWR1Y2lhbCBwb2ludHMgYXJlIHN0b3JlZCBpbiBtYXRsYWIgZm9ybWF0LiBJbiB0aGlzIHN0ZXAsIHdlIHJlYWQgdGhlbSBhbmQgc3RvcmUgdGhlbSBpbiBhIGxpc3QuCgpgYGB7ciByZWFkIGZpZHVjaWFsIHBvaW50c30KI2Z1bmN0aW9uIHRvIHJlYWQgZmlkdWNpYWwgcG9pbnRzCiNpbnB1dDogaW5kZXgKI291dHB1dDogbWF0cml4IG9mIGZpZHVjaWFsIHBvaW50cyBjb3JyZXNwb25kaW5nIHRvIHRoZSBpbmRleApuX2ZpbGVzIDwtIGxlbmd0aChsaXN0LmZpbGVzKHRyYWluX2ltYWdlX2RpciwnKmpwZycpKQpyZWFkTWF0Lm1hdHJpeCA8LSBmdW5jdGlvbihpbmRleCl7CiAgICAgcmV0dXJuKHJvdW5kKHJlYWRNYXQocGFzdGUwKHRyYWluX3B0X2Rpciwgc3ByaW50ZigiJTA0ZCIsIGluZGV4KSwgIi5tYXQiKSlbWzFdXSwwKSkKfQoKI2xvYWQgZmlkdWNpYWwgcG9pbnRzCmZpZHVjaWFsX3B0X2xpc3QgPC0gbGFwcGx5KDE6bl9maWxlcywgcmVhZE1hdC5tYXRyaXgpCnNhdmUoZmlkdWNpYWxfcHRfbGlzdCwgZmlsZT0iLi4vb3V0cHV0L2ZpZHVjaWFsX3B0X2xpc3QuUkRhdGEiKQpgYGAKCiMjIyBTdGVwIDM6IGNvbnN0cnVjdCBmZWF0dXJlcyBhbmQgcmVzcG9uc2VzCgorIFRoZSBmb2xsb3cgcGxvdHMgc2hvdyBob3cgcGFpcndpc2UgZGlzdGFuY2UgYmV0d2VlbiBmaWR1Y2lhbCBwb2ludHMgY2FuIHdvcmsgYXMgZmVhdHVyZSBmb3IgZmFjaWFsIGVtb3Rpb24gcmVjb2duaXRpb24uCgogICsgSW4gdGhlIGZpcnN0IGNvbHVtbiwgNzggZmlkdWNpYWxzIHBvaW50cyBvZiBlYWNoIGVtb3Rpb24gYXJlIG1hcmtlZCBpbiBvcmRlci4gCiAgKyBJbiB0aGUgc2Vjb25kIGNvbHVtbiBkaXN0cmlidXRpb25zIG9mIHZlcnRpY2FsIGRpc3RhbmNlIGJldHdlZW4gcmlnaHQgcHVwaWwoMSkgYW5kICByaWdodCBicm93IHBlYWsoMjEpIGFyZSBzaG93biBpbiAgaGlzdG9ncmFtcy4gRm9yIGV4YW1wbGUsIHRoZSBkaXN0YW5jZSBvZiBhbiBhbmdyeSBmYWNlIHRlbmRzIHRvIGJlIHNob3J0ZXIgdGhhbiB0aGF0IG9mIGEgc3VycHJpc2VkIGZhY2UuCiAgKyBUaGUgdGhpcmQgY29sdW1uIGlzIHRoZSBkaXN0cmlidXRpb25zIG9mIHZlcnRpY2FsIGRpc3RhbmNlcyBiZXR3ZWVuIHJpZ2h0IG1vdXRoIGNvcm5lcig1MCkKYW5kIHRoZSBtaWRwb2ludCBvZiB0aGUgdXBwZXIgbGlwKDUyKS4gIEZvciBleGFtcGxlLCB0aGUgZGlzdGFuY2Ugb2YgYW4gaGFwcHkgZmFjZSB0ZW5kcyB0byBiZSBzaG9ydGVyIHRoYW4gdGhhdCBvZiBhIHNhZCBmYWNlLgoKIVtGaWd1cmUxXSguLi9maWdzL2ZlYXR1cmVfdmlzdWFsaXphdGlvbi5qcGcpCgpgZmVhdHVyZS5SYCBpcyB0aGUgd3JhcHBlciBmb3IgYWxsIGZlYXR1cmUgZW5naW5lZXJpbmcgZnVuY3Rpb25zIGFuZCBvcHRpb25zLiBUaGUgZnVuY3Rpb24gYGZlYXR1cmUoIClgIGhhdmUgb3B0aW9ucyB0aGF0IGNvcnJlc3BvbmQgdG8gZGlmZmVyZW50IHNjZW5hcmlvcyBmb3IgdGhlIHByb2plY3QgYW5kIHByb2R1Y2VzIGFuIFIgb2JqZWN0IHRoYXQgY29udGFpbnMgZmVhdHVyZXMgYW5kIHJlc3BvbnNlcyB0aGF0IGFyZSByZXF1aXJlZCBieSBhbGwgdGhlIG1vZGVscyB0aGF0IGFyZSBnb2luZyB0byBiZSBldmFsdWF0ZWQgbGF0ZXIuIAogIAogICsgYGZlYXR1cmUuUmAKICArIElucHV0OiBsaXN0IG9mIGltYWdlcyBvciBmaWR1Y2lhbCBwb2ludAogICsgT3V0cHV0OiBhbiBSRGF0YSBmaWxlIHRoYXQgY29udGFpbnMgZXh0cmFjdGVkIGZlYXR1cmVzIGFuZCBjb3JyZXNwb25kaW5nIHJlc3BvbnNlcwoKYGBge3IgZmVhdHVyZX0Kc291cmNlKCIuLi9saWIvZmVhdHVyZS5SIikKdG1fZmVhdHVyZV90cmFpbiA8LSBOQQppZihydW4uZmVhdHVyZS50cmFpbil7CiAgdG1fZmVhdHVyZV90cmFpbiA8LSBzeXN0ZW0udGltZShkYXRfdHJhaW48LWZlYXR1cmUoZmlkdWNpYWxfcHRfbGlzdCx0cmFpbl9pZHgsIHJ1bi5wb2x5LmZlYXR1cmUsIHJ1bi5hZGQucG9seS5mZWF0dXJlKSkKICBzYXZlKGRhdF90cmFpbiwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdHJhaW4uUkRhdGEiKQp9ZWxzZXsKICBsb2FkKGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKfQoKdG1fZmVhdHVyZV90ZXN0IDwtIE5BCmlmKHJ1bi5mZWF0dXJlLnRlc3QpewogIHRtX2ZlYXR1cmVfdGVzdCA8LSBzeXN0ZW0udGltZShkYXRfdGVzdCA8LSBmZWF0dXJlKGZpZHVjaWFsX3B0X2xpc3QsIHRlc3RfaWR4LCBydW4ucG9seS5mZWF0dXJlLCBydW4uYWRkLnBvbHkuZmVhdHVyZSkpCiAgc2F2ZShkYXRfdGVzdCwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdGVzdC5SRGF0YSIpCn1lbHNlewogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdGVzdC5SRGF0YSIpCn0KYGBgCgojIyMgU3RlcCA0OiB0cmFpbiBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgd2l0aCB0cmFpbmluZyBmZWF0dXJlcyBhbmQgcmVzcG9uc2VzOyBydW4gdGVzdCBvbiB0ZXN0IGltYWdlcwoKQ2FsbCB0aGUgdHJhaW4gbW9kZWwgYW5kIHRlc3QgbW9kZWwgZnJvbSBsaWJyYXJ5LiAKCmB0cmFpbi5SYCBhbmQgYHRlc3QuUmAgYXJlIHdyYXBwZXJzIGZvciBhbGwgbW9kZWwgdHJhaW5pbmcgc3RlcHMgYW5kIGNsYXNzaWZpY2F0aW9uL3ByZWRpY3Rpb24gc3RlcHMuIAoKKyBgdHJhaW4uUmAKICArIElucHV0OiBhIGRhdGEgZnJhbWUgY29udGFpbmluZyBmZWF0dXJlcyBhbmQgbGFiZWxzIGFuZCBhIHBhcmFtZXRlciBsaXN0LgogICsgT3V0cHV0OmEgdHJhaW5lZCBtb2RlbAorIGB0ZXN0LlJgCiAgKyBJbnB1dDogdGhlIGZpdHRlZCBjbGFzc2lmaWNhdGlvbiBtb2RlbCB1c2luZyB0cmFpbmluZyBkYXRhIGFuZCBwcm9jZXNzZWQgZmVhdHVyZXMgZnJvbSB0ZXN0aW5nIGltYWdlcyAKICArIElucHV0OiBhbiBSIG9iamVjdCB0aGF0IGNvbnRhaW5zIGEgdHJhaW5lZCBjbGFzc2lmaWVyLgogICsgT3V0cHV0OiB0cmFpbmluZyBtb2RlbCBzcGVjaWZpY2F0aW9uCgorIEluIHRoaXMgU3RhcnRlciBDb2RlLCB3ZSB1c2UgbG9naXN0aWMgcmVncmVzc2lvbiB3aXRoIExBU1NPIHBlbmFsdHkgdG8gZG8gY2xhc3NpZmljYXRpb24uIAoKYGBge3IgbG9hZGxpYn0Kc291cmNlKCIuLi9saWIvdHJhaW4uUiIpIApzb3VyY2UoIi4uL2xpYi90ZXN0LlIiKQpgYGAKCiMgQmFzZWxpbmUgTW9kZWwKCgotLS0tLS0tLVRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4gVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLiBUSElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuLS0tLS0tLS0tLQoKCgoKCiMgQWR2YW5jZWQgTW9kZWwgMTogSW1wcm92ZWQgR0JNIE1vZGVsCgoqIE1vZGVsIFRyYWluaW5nCgpgYGB7cn0KaWYgKHJ1bi5nYm0pewogIGlmIChzYW1wbGUucmV3ZWlnaHQpewogICAgdG1fdHJhaW4gPC0gc3lzdGVtLnRpbWUoZml0X3RyYWluIDwtIHRyYWluX2dibShkYXRfdHJhaW4sIHM9MC4xLCBLPUssIG49Z2JtLm51bXRyZWVzLHcgPSB3ZWlnaHRfdHJhaW4pKQogIH0gZWxzZSB7CiAgICB0bV90cmFpbiA8LSBzeXN0ZW0udGltZShmaXRfdHJhaW4gPC0gdHJhaW5fZ2JtKGRhdF90cmFpbiwgcz0wLjEsIEs9Sywgbj1nYm0ubnVtdHJlZXMsdyA9IE5VTEwpKQogIH0KICAKICAjIHBsb3QgdGhlIHBlcmZvcm1hbmNlCiAgYmVzdC5pdGVyLm9vYiA8LSBnYm0ucGVyZihmaXRfdHJhaW4sbWV0aG9kPSJPT0IiKSAgIyByZXR1cm5zIG91dC1vZi1iYWcgZXN0aW1hdGVkIGJlc3QgbnVtYmVyIG9mIHRyZWVzCiAgcHJpbnQoYmVzdC5pdGVyLm9vYikKICBiZXN0Lml0ZXIuY3YgPC0gZ2JtLnBlcmYoZml0X3RyYWluLG1ldGhvZD0iY3YiKSAgICMgcmV0dXJucyBLLWZvbGQgY3YgZXN0aW1hdGUgb2YgYmVzdCBudW1iZXIgb2YgdHJlZXMKICBwcmludChiZXN0Lml0ZXIuY3YpCiAgCiAgc2F2ZShmaXRfdHJhaW4sIGZpbGU9Ii4uL291dHB1dC9maXRfdHJhaW4uUkRhdGEiKQp9CgpgYGAKCiogRXZhbHVhdGlvbiBvbiBUZXN0IFNldAoKYGBge3J9CmlmKHJ1bi5nYm0pewogIHRtX3Rlc3QgPSBOQQogIGZlYXR1cmVfdGVzdCA8LSBhcy5tYXRyaXgoZGF0X3Rlc3RbLCAxOm5jb2woZGF0X3Rlc3QpLTFdKQogIGlmKHJ1bi50ZXN0KXsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L2ZpdF90cmFpbi5SRGF0YSIpCiAgICB0bV90ZXN0IDwtIHN5c3RlbS50aW1lKHByb2JfcHJlZDwtdGVzdF9nYm0oZml0X3RyYWluLGFzLmRhdGEuZnJhbWUoZmVhdHVyZV90ZXN0KSxuPWJlc3QuaXRlci5jdixwcmVkLnR5cGUgPSAncmVzcG9uc2UnKSkKICAgIAogICAgbGFiZWxfcHJlZCA8LSBjb2xuYW1lcyhwcm9iX3ByZWQpW2FwcGx5KHByb2JfcHJlZCwgMSwgd2hpY2gubWF4KV0KICAgIAogIH0gZWxzZSB7CiAgICB0bV90ZXN0IDwtIHN5c3RlbS50aW1lKHtsYWJlbF9wcmVkIDwtIGFzLmludGVnZXIodGVzdChmaXRfdHJhaW4sIGZlYXR1cmVfdGVzdCwgcHJlZC50eXBlID0gJ2NsYXNzJykpOyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JfcHJlZCA8LSB0ZXN0KGZpdF90cmFpbiwgZmVhdHVyZV90ZXN0LCBwcmVkLnR5cGUgPSAncmVzcG9uc2UnKX0pICAKICB9Cn0KYGBgCgoKLS0tLS0tLS1USElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4gVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLi0tLS0tLS0tLS0KCgojIEFkdmFuY2VkIE1vZGVsIDI6IFJhbmRvbSBGb3Jlc3QKCiogQmFsYW5jZSBUcmFpbiBTZXQKCmBgYHtyfQppZihydW4ucmYpewogICMgdHJhbnNmZXIgbGFiZWwgY29sdW1uIGZyb20gZmFjdG9yIHRvIG51bWVyaWMKICBkYXRfdHJhaW4kbGFiZWwgPC0gYXMubnVtZXJpYyhkYXRfdHJhaW4kbGFiZWwpCiAgZGF0X3Rlc3QkbGFiZWwgPC0gYXMubnVtZXJpYyhkYXRfdGVzdCRsYWJlbCkKCiAgaWYocnVuLmJhbGFuY2VkLmRhdGEpewogICAgZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2U8LVJPU0UobGFiZWx+LiwgZGF0X3RyYWluLHNlZWQ9MjAyMCkkZGF0YQogICAgc2F2ZShkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSwgZmlsZT0iLi4vb3V0cHV0L2JhbGFuY2VkX3RyYWluX2RhdGFfcm9zZS5SRGF0YSIpCiAgICBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlIDwtIFJPU0UobGFiZWx+LiwgZGF0X3Rlc3QsIHNlZWQ9MjAyMCkkZGF0YQogICAgc2F2ZShkYXRfdGVzdF9iYWxhbmNlZF9yb3NlLCBmaWxlID0gIi4uL291dHB1dC9iYWxhbmNlZF90ZXN0X2RhdGFfcm9zZS5SRGF0YSIpCiAgfSBlbHNlIHsKICAgIGxvYWQoZmlsZSA9ICIuLi9vdXRwdXQvYmFsYW5jZWRfdHJhaW5fZGF0YV9yb3NlLlJEYXRhIikKICAgIGxvYWQoZmlsZSA9ICIuLi9vdXRwdXQvYmFsYW5jZWRfdGVzdF9kYXRhX3Jvc2UuUkRhdGEiKQogIH0KICB0YWJsZShkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSRsYWJlbCkKICB0YWJsZShkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKQp9CmBgYAoKCiogVHVuZSBQYXJhbWV0ZXJzIGZvciBSYW5kb20gRm9yZXN0CgpgYGB7cn0KaWYocnVuLnJmKXsKICBzb3VyY2UoIi4uL2xpYi9yYW5kb21fZm9yZXN0X29sZF9mZWF0dXJlLlIiKQogIGlmKHR1bmUucmFuZG9tLmZvcmVzdCl7CiAgICB0aW1lLnJmLnR1bmUgPC0gc3lzdGVtLnRpbWUocmYudHVuZSA8LSByYW5kb21fZm9yZXN0X3R1bmUoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UpKQogICAgc2F2ZShyZi50dW5lLCBmaWxlPSIuLi9vdXRwdXQvcmZfdHVuZS5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHVuZSwgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHVuZV90aW1lLlJEYXRhIikKICB9ZWxzZXsKICAgIGxvYWQoIi4uL291dHB1dC9yZl90dW5lLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90dW5lX3RpbWUuUkRhdGEiKQogIH0KICByZi50dW5lCiAgdGltZS5yZi50dW5lWzFdCn0KYGBgCgptdHJ5ID0gMzA4IGlzIHRoZSBiZXN0LgoKKiBUdW5lIFRyZWVzIGFuZCBOb2RlcwoKYGBge3J9CmlmKHJ1bi5yZil7CiAgc291cmNlKCIuLi9saWIvcmFuZG9tX2ZvcmVzdF9vbGRfZmVhdHVyZS5SIikKICBpZih0dW5lLnJhbmRvbS5mb3Jlc3QpewogICAgIyBUcmFpbiA1MDAgdHJlZXM6CiAgICB0aW1lLnJmLnRyYWluLnRyZWU1MDAgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfNTAwX3RyZWVzIDwtCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzUwMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfNTAwX3RyZWVzLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl81MDBfdHJlZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLnRyZWU1MDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzUwMF90cmVlc190aW1lLlJEYXRhIikKICAgIGNhdCgiNTAwIHRyZWUgdGltZSIsIHRpbWUucmYudHJhaW4udHJlZTUwMCkKICAgICMgVHJhaW4gMTAwMCB0cmVlczoKICAgIHRpbWUucmYudHJhaW4udHJlZTEwMDAgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMTAwMF90cmVlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMTAwMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMTAwMF90cmVlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMTAwMF90cmVlcy5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4udHJlZTEwMDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzEwMDBfdHJlZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjEwMDAgdHJlZSB0aW1lIiwgdGltZS5yZi50cmFpbi50cmVlMTAwMCkKICAgICMgVHJhaW4gMTUwMCB0cmVlczoKICAgIHRpbWUucmYudHJhaW4udHJlZTE1MDAgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMTUwMF90cmVlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMTUwMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMTUwMF90cmVlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMTUwMF90cmVlcy5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4udHJlZTE1MDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzE1MDBfdHJlZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjE1MDAgdHJlZSB0aW1lIiwgdGltZS5yZi50cmFpbi50cmVlMTUwMCkKICAgICMgVHJhaW4gMjAwMCB0cmVlczoKICAgIHRpbWUucmYudHJhaW4udHJlZTIwMDAgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMjAwMF90cmVlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMjAwMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMjAwMF90cmVlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMjAwMF90cmVlcy5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4udHJlZTIwMDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzIwMDBfdHJlZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjIwMDAgdHJlZSB0aW1lIiwgdGltZS5yZi50cmFpbi50cmVlMjAwMCkKICAgICMgVHJhaW4gMjUwMCB0cmVlczoKICAgIHRpbWUucmYudHJhaW4udHJlZTI1MDAgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMjUwMF90cmVlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMjUwMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMjUwMF90cmVlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMjUwMF90cmVlcy5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4udHJlZTI1MDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzI1MDBfdHJlZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjI1MDAgdHJlZSB0aW1lIiwgdGltZS5yZi50cmFpbi50cmVlMjUwMCkKCiAgICAjIFRyYWluIDEwIG5vZGVzCiAgICB0aW1lLnJmLnRyYWluLm5vZGUxMCA8LSBzeXN0ZW0udGltZShyYW5kb21fZm9yZXN0X2ZpdF8xMF9ub2RlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzEwKGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlLG10cnkgPSAzMDgpKQogICAgc2F2ZShyYW5kb21fZm9yZXN0X2ZpdF8xMF9ub2RlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMTBfbm9kZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLm5vZGUxMCwgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMTBfbm9kZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjEwIG5vZGUgdGltZSIsIHRpbWUucmYudHJhaW4ubm9kZTEwKQogICAgIyBUcmFpbiAxNSBub2RlcwogICAgdGltZS5yZi50cmFpbi5ub2RlMTUgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMTVfbm9kZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90cmFpbl8xNShkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMTVfbm9kZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzE1X25vZGVzLlJEYXRhIikKICAgIHNhdmUodGltZS5yZi50cmFpbi5ub2RlMTUsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzE1X25vZGVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIxNSBub2RlIHRpbWUiLCB0aW1lLnJmLnRyYWluLm5vZGUxNSkKICAgICMgVHJhaW4gMjAgbm9kZXMKICAgIHRpbWUucmYudHJhaW4ubm9kZTIwIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzIwX25vZGVzIDwtCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMjAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDMwOCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzIwX25vZGVzLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8yMF9ub2Rlcy5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4ubm9kZTIwLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl8yMF9ub2Rlc190aW1lLlJEYXRhIikKICAgIGNhdCgiMjAgbm9kZSB0aW1lIiwgdGltZS5yZi50cmFpbi5ub2RlMjApCiAgICAjIFRyYWluIDI1IG5vZGVzCiAgICB0aW1lLnJmLnRyYWluLm5vZGUyNSA8LSBzeXN0ZW0udGltZShyYW5kb21fZm9yZXN0X2ZpdF8yNV9ub2RlcyA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0X3RyYWluXzI1KGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlLG10cnkgPSAzMDgpKQogICAgc2F2ZShyYW5kb21fZm9yZXN0X2ZpdF8yNV9ub2RlcywgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMjVfbm9kZXMuUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLm5vZGUyNSwgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMjVfbm9kZXNfdGltZS5SRGF0YSIpCiAgICBjYXQoIjI1IG5vZGUgdGltZSIsIHRpbWUucmYudHJhaW4ubm9kZTI1KQogICAgIyBUcmFpbiAzMCBub2RlcwogICAgdGltZS5yZi50cmFpbi5ub2RlMzAgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMzBfbm9kZXMgPC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90cmFpbl8zMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMzA4KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMzBfbm9kZXMsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzMwX25vZGVzLlJEYXRhIikKICAgIHNhdmUodGltZS5yZi50cmFpbi5ub2RlMzAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzMwX25vZGVzX3RpbWUuUkRhdGEiKQogICAgY2F0KCIzMCBub2RlIHRpbWUiLCB0aW1lLnJmLnRyYWluLm5vZGUzMCkKICB9IGVsc2UgewogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzUwMF90cmVlcy5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fMTAwMF90cmVlcy5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fMTUwMF90cmVlcy5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fMjAwMF90cmVlcy5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fMjUwMF90cmVlcy5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fMTBfbm9kZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzE1X25vZGVzLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl8yMF9ub2Rlcy5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fMjVfbm9kZXMuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluXzMwX25vZGVzLlJEYXRhIikKICB9CgoKCiAgI0Vycm9yIHJhdGUgb2YgZWFjaCBoeXBlcnBhcmFtZXRlcjoKCgoKICAjIEV2YWx1YXRlIGVhY2ggaHlwZXJwYXJhbWV0ZXIKCgogICMgUHJlZGljdGVkIHZhbHVlIGZyb20gNTAwIHRyZWVzJyBtb2RlbDoKICByZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0XzUwMF90cmVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfNTAwX3RyZWVzLCBkYXRfdGVzdCkpKQogICMgRXZhbHVhdGUgNTAwIHRyZWVzOgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpCiAgcmZfQVVDX2JhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuYmFsYW5jZWQpCiAgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkID09IGRhdF90ZXN0JGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSA1MDAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSA1MDAiLCByZl9BVUNfYmFsYW5jZWQsIi5cbiIpCiAgY2F0KCJBY2N1cmFjeShpbWJhbGFuY2VkKSA1MDAiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgNTAwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCgogICMgRXZhbHVhdGlvbiBmcm9tIDEwMDAgdHJlZXMnIG1vZGVsOgogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTAwMF90cmVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTAwMF90cmVlcywgZGF0X3Rlc3QpKSkKCiAgcmZfYWNjdXJhY3lfYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2JhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuYmFsYW5jZWQpCiAgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuaW1iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfaW1iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmltYmFsYW5jZWQpCgogIGNhdCgiQWNjdXJhY3koYmFsYW5jZWQpIDEwMDAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAxMDAwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMTAwMCIsIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIkFVQyhpbWJhbGFuY2VkKSAxMDAwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIyAxNTAwIHRyZWVzCiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8xNTAwX3RyZWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSkpKQogIHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8xNTAwX3RyZWVzLCBkYXRfdGVzdCkpKQoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgMTUwMCIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoYmFsYW5jZWQpIDE1MDAiLCByZl9BVUNfYmFsYW5jZWQsIi5cbiIpCiAgY2F0KCJBY2N1cmFjeShpbWJhbGFuY2VkKSAxNTAwIiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDE1MDAiLHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQoKICAjMjAwMCB0cmVlcwogIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjAwMF90cmVlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjAwMF90cmVlcywgZGF0X3Rlc3QpKSkKCiAgcmZfYWNjdXJhY3lfYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2JhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuYmFsYW5jZWQpCiAgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuaW1iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfaW1iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmltYmFsYW5jZWQpCgogIGNhdCgiQWNjdXJhY3koYmFsYW5jZWQpIDIwMDAiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGJhbGFuY2VkKSAyMDAwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMjAwMCIsIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIkFVQyhpbWJhbGFuY2VkKSAyMDAwIixyZl9BVUNfaW1iYWxhbmNlZCwiLlxuIikKCiAgIyAyNTAwIHRyZWVzCiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8yNTAwX3RyZWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSkpKQogIHJmX3ByZWRpY3RlZF9pbWJhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8yNTAwX3RyZWVzLCBkYXRfdGVzdCkpKQoKICByZl9hY2N1cmFjeV9iYWxhbmNlZCA8LSBtZWFuKHJvdW5kKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2JhbGFuY2VkKSxkYXRfdGVzdCRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICByZl9hY2N1cmFjeV9pbWJhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkgMjUwMCIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoYmFsYW5jZWQpIDI1MDAiLCByZl9BVUNfYmFsYW5jZWQsIi5cbiIpCiAgY2F0KCJBY2N1cmFjeShpbWJhbGFuY2VkKSAyNTAwIiwgcmZfYWNjdXJhY3lfaW1iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiQVVDKGltYmFsYW5jZWQpIDI1MDAiLHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQoKICAjIDEwIG5vZGVzCiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8xMF9ub2RlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTBfbm9kZXMsIGRhdF90ZXN0KSkpCgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSAxMCIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoYmFsYW5jZWQpIDEwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMTAiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgMTAiLHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQoKICAjIDE1IG5vZGVzCiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8xNV9ub2RlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMTVfbm9kZXMsIGRhdF90ZXN0KSkpCgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSAxNSIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoYmFsYW5jZWQpIDE1IiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMTUiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgMTUiLHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQoKICAjIDIwIG5vZGVzCiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8yMF9ub2RlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjBfbm9kZXMsIGRhdF90ZXN0KSkpCgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSAyMCIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoYmFsYW5jZWQpIDIwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMjAiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgMjAiLHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQoKICAjIDI1IG5vZGVzCiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8yNV9ub2RlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMjVfbm9kZXMsIGRhdF90ZXN0KSkpCgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSAyNSIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoYmFsYW5jZWQpIDI1IiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMjUiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgMjUiLHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQoKICAjIDMwIG5vZGVzCiAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKHJhbmRvbV9mb3Jlc3RfdGVzdChyYW5kb21fZm9yZXN0X2ZpdF8zMF9ub2RlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkKICByZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfMzBfbm9kZXMsIGRhdF90ZXN0KSkpCgogIHJmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UkbGFiZWwpKQogIHRwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19iYWxhbmNlZCA8LSBXZWlnaHRlZEFVQyh0cHIuZnByLmJhbGFuY2VkKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdF9iYWxhbmNlZF9yb3NlJGxhYmVsKSkKICB0cHIuZnByLmltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCksZGF0X3Rlc3QkbGFiZWwpCiAgcmZfQVVDX2ltYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5pbWJhbGFuY2VkKQoKICBjYXQoIkFjY3VyYWN5KGJhbGFuY2VkKSAzMCIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoYmFsYW5jZWQpIDMwIiwgcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkgMzAiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCkgMzAiLHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQp9CmBgYAoKKioxNSBOb2RlcyBhbmQgMjUwMCB0cmVlcyBpcyB0aGUgYmVzdC4qKgoKKiBUcmFpbiBSYW5kb20gRm9yZXN0IHdpdGggVHVuZWQgUGFyYW1ldGVycwoKYGBge3J9CmlmKHJ1bi5yZil7CiAgaWYodHJhaW4ucmFuZG9tLmZvcmVzdCl7CiAgICB0aW1lLnJmLnRyYWluLmZpbmFsLmJhbGFuY2VkIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2JhbGFuY2VkIDwtCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90cmFpbihkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gMzA4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlZV9udW1iZXIgPSAyMDAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVfc2l6ZSA9IDE1KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfZmluYWxfYmFsYW5jZWQsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX2JhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICAgIHNhdmUodGltZS5yZi50cmFpbi5maW5hbC5iYWxhbmNlZCwgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fZmluYWxfdGltZV9iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICB0aW1lLnJmLnRyYWluLmZpbmFsLmltYmFsYW5jZWQgPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfZmluYWxfaW1iYWxhbmNlZCA8LQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHJhaW4oZGF0X3RyYWluLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSAzMDgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlZV9udW1iZXIgPSAxMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZV9zaXplID0gMTUpKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLmZpbmFsLmltYmFsYW5jZWQsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX3RpbWVfaW1iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2ltYmFsYW5jZWQsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX2ltYmFsYW5jZWRfb2xkX2ZlYXR1cmUuUkRhdGEiKQogIH0gZWxzZSB7CiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fZmluYWxfYmFsYW5jZWRfb2xkX2ZlYXR1cmUuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX3RpbWVfYmFsYW5jZWRfb2xkX2ZlYXR1cmUuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX3RpbWVfaW1iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fZmluYWxfaW1iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgfQp9CmBgYAoKKiBUZXN0IGFuZCBFdmFsdWF0ZSBSYW5kb20gRm9yZXN0IHdpdGggVHVuZWQgUGFyYW1ldGVycwoKYGBge3J9CmlmKHJ1bi5yZil7CiAgIyBCYWxhbmNlZDoKICBpZih0ZXN0LnJhbmRvbS5mb3Jlc3QpewogIHRpbWUucmYudGVzdC5maW5hbC5iYWxhbmNlZCA8LSBzeXN0ZW0udGltZSgKICAgIHJmX3ByZWRpY3RlZF9iYWxhbmNlZCA8LSBhcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfZmluYWxfYmFsYW5jZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0X2JhbGFuY2VkX3Jvc2UpKSkpCiAgcmZfYWNjdXJhY3lfYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPT0gZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkpCiAgdHByLmZwci5iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCksZGF0X3Rlc3RfYmFsYW5jZWRfcm9zZSRsYWJlbCkKICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKCiAgY2F0KCJBVUMoYmFsYW5jZWQpOiAiLCByZl9BVUNfYmFsYW5jZWQsIi5cbiIpCiAgY2F0KCJBY2N1cmFjeShiYWxhbmNlZCkiLCByZl9hY2N1cmFjeV9iYWxhbmNlZCoxMDAsIiUuXG4iKQogIGNhdCgiVHJhaW5pbmcgdGltZTogIiwgdGltZS5yZi50cmFpbi5maW5hbC5iYWxhbmNlZFsxXSwgInMuXG4iKQogIGNhdCgiVGVzdGluZyB0aW1lOiAiLCB0aW1lLnJmLnRlc3QuZmluYWwuYmFsYW5jZWRbMV0sICJzLlxuIikKCiAgIyBJbWJhbGFuY2VkOgogIHRpbWUucmYudGVzdC5maW5hbC5pbWJhbGFuY2VkIDwtIHN5c3RlbS50aW1lKAogICAgcmZfcHJlZGljdGVkX2ltYmFsYW5jZWQgPC1hcy5udW1lcmljKGFzLnZlY3RvcihyYW5kb21fZm9yZXN0X3Rlc3QocmFuZG9tX2ZvcmVzdF9maXRfZmluYWxfaW1iYWxhbmNlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdF90ZXN0KSkpKQogIHJmX2FjY3VyYWN5X2ltYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfaW1iYWxhbmNlZCA9PSBkYXRfdGVzdCRsYWJlbCkpCiAgdHByLmZwci5pbWJhbGFuY2VkIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocmZfcHJlZGljdGVkX2ltYmFsYW5jZWQpLGRhdF90ZXN0JGxhYmVsKQogIHJmX0FVQ19pbWJhbGFuY2VkIDwtIFdlaWdodGVkQVVDKHRwci5mcHIuaW1iYWxhbmNlZCkKCiAgY2F0KCJBVUMoaW1iYWxhbmNlZCk6ICIsIHJmX0FVQ19pbWJhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koaW1iYWxhbmNlZCkiLCByZl9hY2N1cmFjeV9pbWJhbGFuY2VkKjEwMCwiJS5cbiIpCiAgY2F0KCJUcmFpbmluZyB0aW1lOiAiLCB0aW1lLnJmLnRyYWluLmZpbmFsLmltYmFsYW5jZWRbMV0sICJzLlxuIikKICBjYXQoIlRlc3RpbmcgdGltZTogIiwgdGltZS5yZi50ZXN0LmZpbmFsLmltYmFsYW5jZWRbMV0sICJzLlxuIikKICB9Cn0KYGBgCgotLS0tLS0tLVRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4gVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLiBUSElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuLS0tLS0tLS0tLQoKCgoKIyBBZHZhbmNlZCBNb2RlbCAzOiBTVk0gTW9kZWwKCiogQmFsYW5jZSB0aGUgVHJhaW5pbmcgU2V0CgpgYGB7cn0KaWYocnVuLnN2bSl7CiAgdG1fc3ZtX3JlYmFsYW5jZWRfdHJhaW4gPC0gTkEKICBpZihzYW1wbGUucmV3ZWlnaHQpewogICAgdG1fc3ZtX3JlYmFsYW5jZWRfdHJhaW4gPC0gc3lzdGVtLnRpbWUoc3ZtX3RyYWluaW5nX2RhdGEgPC0gUk9TRShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdHJhaW4pJGRhdGEpCiAgICBzYXZlKHN2bV90cmFpbmluZ19kYXRhLCBmaWxlPSIuLi9vdXRwdXQvc3ZtX3RyYWluaW5nX2RhdGEuUkRhdGEiKQogICAgc2F2ZSh0bV9zdm1fcmViYWxhbmNlZF90cmFpbiwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9yZWJhbGFuY2VkX3RyYWluLlJEYXRhIikKICB9IGVsc2UgewogICAgc3ZtX3RyYWluaW5nX2RhdGEgPC0gZGF0X3RyYWluCiAgICB0bV9zdm1fcmViYWxhbmNlZF90cmFpbiA8LSB0bV9mZWF0dXJlX3RyYWluCiAgfQp9ICNlbHNlIHsKICAjbG9hZChmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX3JlYmFsYW5jZWRfdHJhaW4uUkRhdGEiKQojfQpgYGAKCiogTW9kZWwgU2VsZWN0aW9uCgpgYGB7cn0KaWYocnVuLnN2bSl7CiAgdG1fc3ZtX2xpbmVhcl9tb2QgPC0gTkEKICB0bV9zdm1fcmFkaWFsX21vZCA8LSBOQQogIAogIGlmKG1vZGVsLnNlbGVjdGlvbil7CiAgICBzdm1fbW9kZWxfYXVjIDwtIHJlcChOQSwgMikKICAgIAogICAgIyMjIGxpbmVhciBrZXJuZWwKICAgIGlmKHJ1bi5jdil7CiAgICAgICNiZXN0LmxpbmVhci5jb3N0IDwtIHN2bV9saW5lYXJfY29zdF90dW5lKHN2bV90cmFpbmluZ19kYXRhKQogICAgICAjY2F0KCJUaGUgYmVzdCBjb3N0IGZvciBzdm0gbW9kZWwgd2l0aCBsaW5lYXIga2VybmVsIGlzOiAiLCBiZXN0LmxpbmVhci5jb3N0JGJlc3QucGFyYW1ldGVycyRjb3N0KQogICAgICB0bV9zdm1fbGluZWFyX21vZCA8LSBzeXN0ZW0udGltZShzdm1fbGluZWFyX21vZCA8LSBzdm1fbGluZWFyX3RyYWluKHN2bV90cmFpbmluZ19kYXRhLCAwLjAxLCBLKSkKICAgICAgc2F2ZShzdm1fbGluZWFyX21vZCwgZmlsZT0iLi4vb3V0cHV0L3N2bV9saW5lYXJfbW9kLlJEYXRhIikKICAgICAgc2F2ZSh0bV9zdm1fbGluZWFyX21vZCwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9saW5lYXJfbW9kLlJEYXRhIikKICAgIH0gZWxzZSB7CiAgICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3N2bV9saW5lYXJfbW9kLlJEYXRhIikKICAgICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgfQogICAgc3ZtX2xpbmVhcl9wcmVkIDwtIHN2bV90ZXN0KHN2bV9saW5lYXJfbW9kLCBzdm1fdHJhaW5pbmdfZGF0YSwgVFJVRSkKICAgICNtZWFuKHJvdW5kKHN2bV9saW5lYXJfcHJlZCA9PSBzdm1fdHJhaW5pbmdfZGF0YSRsYWJlbCkpCiAgICBzdm1fbGluZWFyX2FjY3UgPC0gbWVhbihyb3VuZChzdm1fbGluZWFyX3ByZWQgPT0gc3ZtX3RyYWluaW5nX2RhdGEkbGFiZWwpKQogICAgdHByLmZwcl9saW5lYXIgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhzdm1fbGluZWFyX3ByZWQpLCBzdm1fdHJhaW5pbmdfZGF0YSRsYWJlbCkKICAgIHN2bV9tb2RlbF9hdWNbMV0gPC0gV2VpZ2h0ZWRBVUModHByLmZwcl9saW5lYXIpCiAgICAKICAgIAogICAgIyMjIHJhZGlhbCBiYXNpcyBrZXJuZWwKICAgIGlmKHJ1bi5jdil7CiAgICAgICNiZXN0LnJhZGlhbC5jb3N0IDwtIHN2bV9yYWRpYWxfY29zdF90dW5lKHN2bV90cmFpbmluZ19kYXRhKQogICAgICAjcmFkaWFsX2Nvc3QgPSBiZXN0LnJhZGlhbC5jb3N0JGJlc3QucGFyYW1ldGVycyRjb3N0CiAgICAgICNyYWRpYWxfZ2FtbWEgPSBiZXN0LnJhZGlhbC5jb3N0JGJlc3QucGFyYW1ldGVycyRnYW1tYQogICAgICAKICAgICAgdG1fc3ZtX3JhZGlhbF9tb2QgPCBzeXN0ZW0udGltZShzdm1fcmFkaWFsX21vZCA8LSBzdm1fcmFkaWFsX3RyYWluKHN2bV90cmFpbmluZ19kYXRhLCAxLCBLKSkKICAgICAgc2F2ZShzdm1fcmFkaWFsX21vZCwgZmlsZT0iLi4vb3V0cHV0L3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKICAgICAgc2F2ZSh0bV9zdm1fcmFkaWFsX21vZCwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKICAgIH0gZWxzZSB7IAogICAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKICAgIH0KICAgIHN2bV9yYWRpYWxfcHJlZCA8LSBzdm1fdGVzdChzdm1fcmFkaWFsX21vZCwgc3ZtX3RyYWluaW5nX2RhdGEsIFRSVUUpCiAgICAjIGV2YWx1YXRlIHBlcmZvcm1hbmNlCiAgICBzdm1fcmFkaWFsX2FjY3UgPC0gbWVhbihyb3VuZChzdm1fcmFkaWFsX3ByZWQgPT0gc3ZtX3RyYWluaW5nX2RhdGEkbGFiZWwpKQogICAgdHByLmZwcl9kZWZhdWx0IDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMoc3ZtX3JhZGlhbF9wcmVkKSwgc3ZtX3RyYWluaW5nX2RhdGEkbGFiZWwpCiAgICBzdm1fbW9kZWxfYXVjWzJdIDwtIFdlaWdodGVkQVVDKHRwci5mcHJfZGVmYXVsdCkKICB9IGVsc2UgewogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvc3ZtX3JhZGlhbF9tb2QuUkRhdGEiKQogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX3JhZGlhbF9tb2QuUkRhdGEiKQogIH0KICAKICAKICAjIyMgRXZhbHVhdGlvbiBvbiBUZXN0aW5nIERhdGEKICB0bV9zdm1fcmViYWxhbmNlZF90ZXN0IDwtIE5BCiAgaWYoc2FtcGxlLnJld2VpZ2h0KXsKICAgIHRtX3N2bV9yZWJhbGFuY2VkX3Rlc3QgPC0gc3lzdGVtLnRpbWUoc3ZtX3Rlc3RpbmdfZGF0YSA8LSBST1NFKGxhYmVsIH4gLiwgZGF0YSA9IGRhdF90ZXN0KSRkYXRhKQogICAgc2F2ZShzdm1fdGVzdGluZ19kYXRhLCBmaWxlPSIuLi9vdXRwdXQvc3ZtX3Rlc3RpbmdfZGF0YS5SRGF0YSIpCiAgICBzYXZlKHRtX3N2bV9yZWJhbGFuY2VkX3Rlc3QsIGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmViYWxhbmNlZF90ZXN0LlJEYXRhIikKICB9IGVsc2UgewogICAgc3ZtX3Rlc3RpbmdfZGF0YSA8LSBkYXRfdGVzdAogICAgdG1fc3ZtX3JlYmFsYW5jZWRfdGVzdCA8LSB0bV9mZWF0dXJlX3Rlc3QKICB9CiAgCiAgaWYocnVuLnN2bS50ZXN0KXsKICAgIHN2bV9hdWMgPC0gcmVwKE5BLCAyKQogICAgc3ZtX2FjY3UgPC0gcmVwKE5BLCAyKQogICAgIyMgbGluZWFyCiAgICB0bV9zdm1fbGluZWFyX3Rlc3QgPC0gc3lzdGVtLnRpbWUoc3ZtX2xpbmVhcl9wcmVkIDwtIHN2bV90ZXN0KHN2bV9saW5lYXJfbW9kLCBzdm1fdGVzdGluZ19kYXRhKSkKICAgIHN2bV9hY2N1WzFdID0gbWVhbihyb3VuZChzdm1fbGluZWFyX3ByZWQgPT0gc3ZtX3Rlc3RpbmdfZGF0YSRsYWJlbCkpCiAgICB0cHIuZnByLmxpbmVhciA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHN2bV9saW5lYXJfcHJlZCksIHN2bV90ZXN0aW5nX2RhdGEkbGFiZWwpCiAgICBzdm1fYXVjWzFdID0gV2VpZ2h0ZWRBVUModHByLmZwci5saW5lYXIpCiAgICAjIyByYmYKICAgIHRtX3N2bV9yYmZfdGVzdCA8LSBzeXN0ZW0udGltZShzdm1fcmJmX3ByZWQgPC0gc3ZtX3Rlc3Qoc3ZtX3JhZGlhbF9tb2QsIHN2bV90ZXN0aW5nX2RhdGEpKQogICAgc3ZtX2FjY3VbMl0gPSBtZWFuKHJvdW5kKHN2bV9yYmZfcHJlZCA9PSBzdm1fdGVzdGluZ19kYXRhJGxhYmVsKSkKICAgIHRwci5mcHIucmJmIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMoc3ZtX2xpbmVhcl9wcmVkKSwgc3ZtX3Rlc3RpbmdfZGF0YSRsYWJlbCkKICAgIHN2bV9hdWNbMl0gPSBXZWlnaHRlZEFVQyh0cHIuZnByLnJiZikKICAgIAogICAgc2F2ZSh0bV9zdm1fcmFkaWFsX21vZCwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9saW5lYXJfdGVzdC5SRGF0YSIpCiAgICAKICAgICMjIHBlcmZvcm1hbmNlCiAgICBzdm1fYXVjCiAgICBzdm1fYWNjdQoKICAgIGNhdCgiVGhlIGFjY3VyYWN5IG9mIHN2bSBtb2RlbCBpcyIsIHN2bV9hY2N1WzJdKjEwMCwgIiUuXG4iKQogICAgY2F0KCJUaGUgQVVDIG9mIHN2bSBtb2RlbCBpcyIsIHN2bV9hdWNbMl0sICIuXG4iKQogIH0gZWxzZSB7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmViYWxhbmNlZF90ZXN0LlJEYXRhIikKICB9Cn0jIGVsc2UgewogICNsb2FkKGZpbGU9Ii4uL291dHB1dC9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgI2xvYWQoZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKI30KYGBgCgoqIFN1bW1hcml6ZSBSdW5uaW5nIFRpbWUKCmBgYHtyfQppZihydW4uc3ZtKXsKICAjY2F0KCJUaW1lIGZvciByZWJhbGFuY2luZyB0cmFpbmluZyBkYXRhID0iLCB0bV9zdm1fcmViYWxhbmNlZF90cmFpblsxXSwgInMgXG4iKQogICNjYXQoIlRpbWUgZm9yIHJlYmFsYW5jaW5nIHRlc3RpbmcgZGF0YSA9IiwgdG1fc3ZtX3JlYmFsYW5jZWRfdGVzdFsxXSwgInMgXG4iKQogICNjYXQoIlRpbWUgZm9yIHRyYWluaW5nIHN2bSBtb2RlbCA9IiwgdG1fc3ZtX3JhZGlhbF9tb2RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRlc3Rpbmcgc3ZtIG1vZGVsPSIsIHRtX3N2bV9yYmZfdGVzdFsxXSwgInMgXG4iKQp9CmBgYAoKLS0tLS0tLS1USElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4gVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLi0tLS0tLS0tLS0KCgoKCiMgQWR2YW5jZWQgTW9kZWwgNDogUmlkZ2UgTW9kZWwKCiogQXBwbHkgQ29uc3RydWN0ZWQgUmlkZ2UgTW9kZWwgdG8gdGhlIFRyYWluaW5nIFNldAoKYGBge3J9CmlmKHJ1bi5yaWRnZSl7CiAgdG1fcmlkZ2VfdHJhaW4gPC0gTkEKICBpZiAodHJhaW4ucmlkZ2UpewogICAgZGF0X3RyYWluX3JlYmFsYW5jZWQgPC0gUk9TRShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdHJhaW4sIHNlZWQ9MjAyMCkkZGF0YQogICAgdG1fcmlkZ2VfdHJhaW4gPC0gc3lzdGVtLnRpbWUocmlkZ2VfY3ZfbW9kZWw8LXJpZGdlX3RyYWluKHRyYWluX2RhdGE9ZGF0X3RyYWluX3JlYmFsYW5jZWQsIGFscGhhPWFscGhhLCBLPUssIGxhbWJkYT1sYW1iZGEpKQogICAgc2F2ZShyaWRnZV9jdl9tb2RlbCwgZmlsZT0iLi4vb3V0cHV0L3JpZGdlX2N2X21vZGVsLlJEYXRhIikKICAgIHNhdmUodG1fcmlkZ2VfdHJhaW4sIGZpbGU9Ii4uL291dHB1dC9yaWRnZV90cmFpbl90aW1lLlJEYXRhIikKICB9IGVsc2UgewogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfY3ZfbW9kZWwuUkRhdGEiKQogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfdHJhaW5fdGltZS5SRGF0YSIpCiAgfQp9CmBgYAoKKiBVc2UgQ3Jvc3MtVmFsaWRhdGlvbiB0byBDaG9vc2UgdGhlIE9wdGltYWwgTGFtYmRhIHdpdGggU21hbGxlc3QgTVNFCgpgYGB7cn0KaWYocnVuLnJpZGdlKXsKICBpZiAocnVuLmN2KXsKICAgIHNldC5zZWVkKDIwMjApCiAgICBmZWF0dXJlX3RyYWluID0gYXMubWF0cml4KGRhdF90cmFpblssIC02MDA3XSkKICAgIGxhYmVsX3RyYWluID0gYXMuaW50ZWdlcihkYXRfdHJhaW4kbGFiZWwpCiAgICByaWRnZV9tb2RlbCA9IGN2LmdsbW5ldCh4PWZlYXR1cmVfdHJhaW4sIHk9bGFiZWxfdHJhaW4sIGFscGhhPWFscGhhLCBuZm9sZHM9SywgbGFtYmRhPWxhbWJkYSkKICAgIG9wdF9sYW1iZGEgPSByaWRnZV9tb2RlbCRsYW1iZGEubWluCiAgICBzYXZlKG9wdF9sYW1iZGEsIGZpbGU9Ii4uL291dHB1dC9yaWRnZV9vcHRpbWFsX2xhbWJkYS5SRGF0YSIpCiAgfSBlbHNlIHsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JpZGdlX29wdGltYWxfbGFtYmRhLlJEYXRhIikKICB9Cn0KYGBgCgoqIFByZWRpY3Qgb24gVGVzdGluZyBTZXQgd2l0aCB0aGUgT3B0aW1hbCBMYW1iZGEKCmBgYHtyfQppZihydW4ucmlkZ2UpewogIHRtX3JpZGdlX3Rlc3QgPSBOQQogIGlmKHJ1bi50ZXN0KXsKICAgIGxvYWQoIi4uL291dHB1dC9yaWRnZV9jdl9tb2RlbC5SRGF0YSIpCiAgICBmZWF0dXJlX3Rlc3QgPC0gYXMubWF0cml4KGRhdF90ZXN0WywgLTYwMDddKQogICAgdG1fcmlkZ2VfdGVzdCA8LSBzeXN0ZW0udGltZShsYWJlbF9wcmVkPC1hcy5pbnRlZ2VyKHJpZGdlX3Rlc3QobW9kZWw9cmlkZ2VfY3ZfbW9kZWwsIGZlYXR1cmVzPWZlYXR1cmVfdGVzdCwgcHJlZC50eXBlID0gJ2NsYXNzJykpKQogICAgc2F2ZSh0bV9yaWRnZV90ZXN0LCBmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfdGVzdF90aW1lLlJEYXRhIikKICB9IGVsc2V7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9yaWRnZV90ZXN0X3RpbWUuUkRhdGEiKQogIH0KfQpgYGAKCiogU3VtbWFyaXplIFJ1bm5pbmcgVGltZQoKYGBge3J9CmlmKHJ1bi5yaWRnZSl7CiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdHJhaW5pbmcgZmVhdHVyZXM9IiwgdG1fZmVhdHVyZV90cmFpblsxXSwgInMgXG4iKQogIGNhdCgiVGltZSBmb3IgY29uc3RydWN0aW5nIHRlc3RpbmcgZmVhdHVyZXM9IiwgdG1fZmVhdHVyZV90ZXN0WzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciB0cmFpbmluZyByaWRnZSBtb2RlbD0iLCB0bV9yaWRnZV90cmFpblsxXSwgInMgXG4iKSAKICBjYXQoIlRpbWUgZm9yIHRlc3RpbmcgcmlkZ2UgbW9kZWw9IiwgdG1fcmlkZ2VfdGVzdFsxXSwgInMgXG4iKQp9CmBgYAoKKiBFdmFsdWF0aW9uIG9uIEluZGVwZW5kZW50IFRlc3RpbmcgRGF0YSAKCmBgYHtyfQppZihydW4ucmlkZ2UpewogIGxvYWQoIi4uL291dHB1dC9yaWRnZV9jdl9tb2RlbC5SRGF0YSIpCiAgZmVhdHVyZV90ZXN0IDwtIGFzLm1hdHJpeChkYXRfdGVzdFssIC02MDA3XSkKICBsYWJlbF9wcmVkID0gYXMuaW50ZWdlcihwcmVkaWN0KHJpZGdlX2N2X21vZGVsLCBzPW9wdF9sYW1iZGEsIG5ld3g9ZmVhdHVyZV90ZXN0LCB0eXBlPSdjbGFzcycpKQogIGxhYmVsX3Rlc3QgPSBhcy5pbnRlZ2VyKGRhdF90ZXN0JGxhYmVsKQogIGNvbXBhcmUgPC0gY2JpbmQgKGxhYmVsX3Rlc3QsIGxhYmVsX3ByZWQpCiAgcmlkZ2VfYWNjdXJhY3kgPSBtZWFuKGFwcGx5KGNvbXBhcmUsIDEsIG1pbikvYXBwbHkoY29tcGFyZSwgMSwgbWF4KSkgCiAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgdGhlIHJpZGdlIG1vZGVsIGlzIiwgcmlkZ2VfYWNjdXJhY3kqMTAwLCAiJS5cbiIpCiAgcmlkZ2VfQVVDID0gYXVjKHJvYyhsYWJlbF9wcmVkLGxhYmVsX3Rlc3QpKQogIGNhdCgiVGhlIEFVQyBvZiB0aGUgcmlkZ2UgbW9kZWwgaXMiLCByaWRnZV9BVUMsICIuXG4iKQp9CmBgYAoKLS0tLS0tLS1USElTIElTIFRPIFNFUEFSQVRFIEVBQ0ggTU9ERUwuIFRISVMgSVMgVE8gU0VQQVJBVEUgRUFDSCBNT0RFTC4gVEhJUyBJUyBUTyBTRVBBUkFURSBFQUNIIE1PREVMLi0tLS0tLS0tLS0KCgoKCiMgQWR2YW5jZWQgTW9kZWwgNTogUENBICsgTERBCgoqIFJlYmFsYW5jZSBUcmFpbmluZyBTZXQKCmBgYHtyfQppZihydW4ucGNhX2xkYSl7CiAgaWYoc2FtcGxlLnJld2VpZ2h0KXsKICAgIGJhbGFuY2VkX3RyYWluX2RhdGEgPC0gUk9TRShsYWJlbH4uLGRhdGEgPSBkYXRfdHJhaW4pJGRhdGEKICAgIHNhdmUoYmFsYW5jZWRfdHJhaW5fZGF0YSwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfYmFsYW5jZWRfdHJhaW4uUkRhdGEiKQogIH0gZWxzZSB7CiAgICBsb2FkKGJhbGFuY2VkX3RyYWluX2RhdGEsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKICB9Cn0KYGBgCgoqIFBlcmZvcm0gUENBIGZvciBEaW1lbnNpb24gUmVkdWN0aW9uCgoqKlNpbmNlIHRoZXJlIGFyZSBvdmVyIDYwMDAgZmVhdHVyZXMsIHdlIGltcGxlbWVudCB0aGUgUENBIG1ldGhvZCB0byByZWR1Y2UgZGltZW5zaW9uIGFjY29yZGluZyB0byB0aGUgY292YXJpYW5jZSBtYXRyaXguIFdlIG9ubHkgcmV0YWluIFBDcyB3aXRoIGxhcmdlIHZhcmlhbmNlLioqCgpgYGB7ciBwY2EgbGRhfQppZihydW4ucGNhX2xkYSl7CiAgaWYocnVuLnBjYV9sYWQudGVzdCl7CiAgICBpZihzYW1wbGUucmV3ZWlnaHQpewogICAgICBiYWxhbmNlZF90ZXN0X2RhdGEgPC0gUk9TRShsYWJlbH4uLGRhdGEgPSBkYXRfdGVzdCkkZGF0YQogICAgICBzYXZlKGJhbGFuY2VkX3Rlc3RfZGF0YSwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfYmFsYW5jZWRfdGVzdC5SRGF0YSIpCiAgICB9IGVsc2UgewogICAgICBsb2FkKGJhbGFuY2VkX3Rlc3RfZGF0YSwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfYmFsYW5jZWRfdGVzdC5SRGF0YSIpCiAgICB9CiAgfQogIAogIGlmKHJ1bi5zZWxlY3RfUEMpewogICAgI3NlcGFyYXRlIHRoZSBmZWF0dXJlcyBmcm9tIGxhYmVsCiAgICBkYXRfdHJhaW5fbmV3IDwtIGJhbGFuY2VkX3RyYWluX2RhdGFbLC1kaW0oYmFsYW5jZWRfdHJhaW5fZGF0YSlbMl1dCiAgICBkYXRfdGVzdF9uZXcgPC0gYmFsYW5jZWRfdGVzdF9kYXRhWywtZGltKGJhbGFuY2VkX3Rlc3RfZGF0YSlbMl1dCiAgICAjY3JlYXRlIGEgdmVjdG9yIGNvbnRhaW4gdGFyZ2V0IG51bWJlciBvZiBQQ3MKICAgIG51bS5wY2EgPC0gYygxMCw1MCw1MDAsMTAwMCkKICAgIHRyYWluX3BjYSA8LSBmdW5jdGlvbihudW0ucGNhKXsKICAgICAgZm9yKGkgaW4gMTpsZW5ndGgobnVtLnBjYSkpewogICAgICAgICNzdGFydCB0aW1lIGZvciB0cmFpbmluZyB0aGUgbW9kZWwKICAgICAgICB0cmFpbi5tb2RlbC5zdGFydCA9IHByb2MudGltZSgpCiAgICAgICAgI3J1biBQQ0EKICAgICAgICBwY2EgPC0gcHJjb21wKGRhdF90cmFpbl9uZXcpCiAgICAgICAgI3N0b3JlIGZvciBlYWNoIHBvdGVudGlhbCBQQwogICAgICAgIHRyYWluX3BjYSA8LSBkYXRhLmZyYW1lKHBjYSR4WywxOm51bS5wY2FbaV1dLCBsYWJlbCA9IGJhbGFuY2VkX3RyYWluX2RhdGFbZGltKGJhbGFuY2VkX3RyYWluX2RhdGEpWzJdXSkKICAgICAgICBwcmVkX3BjYSA8LSBwcmVkaWN0KHBjYSxkYXRfdGVzdF9uZXcpCiAgICAgICAgdGVzdF9wY2EgPC0gZGF0YS5mcmFtZShwcmVkX3BjYVssMTpudW0ucGNhW2ldXSwgbGFiZWwgPSBiYWxhbmNlZF90ZXN0X2RhdGFbZGltKGJhbGFuY2VkX3Rlc3RfZGF0YSlbMl1dKQogICAgICAgICNmaXR0aW5nIHRoZSBsZGEgbW9kZWwKICAgICAgICBsZGFfcGNhIDwtIGxkYShsYWJlbCB+IC4sIGRhdGEgPSB0cmFpbl9wY2EpIAogICAgICAgICNzdG9wIHRpbWUgZm9yIHRyYWluaW5nIHRoZSBtb2RlbAogICAgICAgIHRyYWluLm1vZGVsLmVuZCA9IHByb2MudGltZSgpCiAgICAgICAgI3N0YXJ0IHRpbWUgZm9yIHRlc3RpbmcgdGhlIG1vZGVsCiAgICAgICAgdGVzdC5tb2RlbC5zdGFydCA9IHByb2MudGltZSgpCiAgICAgICAgI3ByZWRpY3QgbGRhIG1vZGVsCiAgICAgICAgbGRhX3ByZWRfcGNhID0gcHJlZGljdChsZGFfcGNhLHRlc3RfcGNhWy1kaW0odGVzdF9wY2EpWzJdXSkKICAgICAgICAjZW5kIHRpbWUgZm9yIHRlc3RpbmcgdGhlIG1vZGVsCiAgICAgICAgdGVzdC5tb2RlbC5lbmQgPSBwcm9jLnRpbWUoKQogICAgICAgICN0ZXN0IGFjY3VyYWN5CiAgICAgICAgdGVzdF9hY2N1cmFjeT1jb25mdXNpb25NYXRyaXgobGRhX3ByZWRfcGNhJGNsYXNzLCB0ZXN0X3BjYSRsYWJlbCkkb3ZlcmFsbFsxXQogICAgICAgIHByaW50KGxpc3QobDE9dHJhaW4ubW9kZWwuZW5kIC0gdHJhaW4ubW9kZWwuc3RhcnQsCiAgICAgICAgICAgICAgIGwyPXRlc3QubW9kZWwuZW5kIC0gdGVzdC5tb2RlbC5zdGFydCwKICAgICAgICAgICAgICAgbDM9dGVzdF9hY2N1cmFjeSkpCiAgICAgIH0KICAgIH0KICB0cmFpbl9wY2EobnVtLnBjYSkKICB9Cn0KYGBgCgoqKkJ5IGNvbXBhcmluZyB0aGUgdHJhaW5pbmcgdGltZSwgdGVzdCB0aW1lIGFuZCBhY2N1cmFjeSwgd2UgdXNlIG1vZGVsIHdpdGggNTAgUENzLioqCgoqIE1vZGVsIFRyYWluaW5nCgpgYGB7ciBmaW5hbCBtb2RlbH0KaWYocnVuLnBjYV9sZGEpewogIHRyYWluLm1vZGVsLnN0YXJ0ID0gcHJvYy50aW1lKCkKICBpZihydW4ubGRhKXsKICAgIHBjYV8xMCA8LSBwcmNvbXAoZGF0X3RyYWluX25ldykKICAgIHRyYWluX3BjYV8xMCA8LSBkYXRhLmZyYW1lKHBjYV8xMCR4WywxOjUwXSwgbGFiZWwgPSBiYWxhbmNlZF90cmFpbl9kYXRhW2RpbShiYWxhbmNlZF90cmFpbl9kYXRhKVsyXV0pCiAgICBwcmVkX3BjYV8xMCA8LSBwcmVkaWN0KHBjYV8xMCxkYXRfdGVzdF9uZXcpCiAgICB0ZXN0X3BjYV8xMCA8LSBkYXRhLmZyYW1lKHByZWRfcGNhXzEwWywxOjUwXSwgbGFiZWwgPSBiYWxhbmNlZF90ZXN0X2RhdGFbZGltKGJhbGFuY2VkX3Rlc3RfZGF0YSlbMl1dKQogICAgc2F2ZSh0cmFpbl9wY2FfMTAsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3BjYV90cmFpbi5SRGF0YSIpCiAgICBzYXZlKHRlc3RfcGNhXzEwLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9wY2FfdGVzdC5SRGF0YSIpICAKICB9IGVsc2UgewogICAgbG9hZCh0cmFpbl9wY2FfMTAsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3BjYV90cmFpbi5SRGF0YSIpCiAgICBsb2FkKHRlc3RfcGNhXzEwLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9wY2FfdGVzdC5SRGF0YSIpICAKICB9CiAgI2NhbGN1bGF0ZSB0aGUgdHJhaW5pbmcgdGltZQogIGxkYV9wY2FfMTAgPC0gbGRhKGxhYmVsIH4gLiwgZGF0YSA9IHRyYWluX3BjYV8xMCwgY3YgPSBUUlVFKQogIHRyYWluLm1vZGVsLmVuZCA9IHByb2MudGltZSgpCn0KYGBgCgoqIENhbGN1bGF0ZSB0aGUgVHJhaW5pbmcgYW5kIFRlc3RpbmcgQWNjdXJhY3kgb2YgTERBIE1vZGVsCgpgYGB7cn0KaWYocnVuLnBjYV9sZGEpewogIHRlc3QubW9kZWwuc3RhcnQgPSBwcm9jLnRpbWUoKQogIHByZWRfdHJhaW5fbGRhIDwtIHByZWRpY3QobGRhX3BjYV8xMCwgdHJhaW5fcGNhXzEwWy1kaW0odHJhaW5fcGNhXzEwKVsyXV0pCiAgYWNjdV90cmFpbl9sZGEgPC0gbWVhbihwcmVkX3RyYWluX2xkYSRjbGFzcyA9PSB0cmFpbl9wY2FfMTAkbGFiZWwpCiAgY2F0KCJUaGUgdHJhaW5pZyBhY2N1cmFjeSBvZiBtb2RlbDogTERBIiwgImlzIiwgYWNjdV90cmFpbl9sZGEqMTAwLCAiJS5cbiIpCiAgI2NhbGN1bGF0aW5nIHRoZSB0ZXN0IHRpbWUKICBpZihydW4udGVzdCl7CiAgICBwcmVkX3Rlc3RfbGRhIDwtIHByZWRpY3QobGRhX3BjYV8xMCwgdGVzdF9wY2FfMTApCiAgfQogIHRlc3QubW9kZWwuZW5kID0gcHJvYy50aW1lKCkKICBzYXZlKHByZWRfdGVzdF9sZGEsIGZpbGU9Ii4uL291dHB1dC9maXRfdHJhaW4uUkRhdGEiKQogIGFjY3VfdGVzdF9sZGEgPC0gbWVhbihwcmVkX3Rlc3RfbGRhJGNsYXNzID09IHRlc3RfcGNhXzEwJGxhYmVsKQogIGNhdCgiVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBMREEiLCAiaXMiLCBhY2N1X3Rlc3RfbGRhKjEwMCwgIiUuXG4iKQogIHRwci5mcHIgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhwcmVkX3Rlc3RfbGRhJGNsYXNzKSwgdGVzdF9wY2FfMTAkbGFiZWwpCiAgbGRhX2F1YyA9IFdlaWdodGVkQVVDKHRwci5mcHIpCiAgY2F0KCJUaGUgQVVDIG9mIG1vZGVsOiBMREEgaXMiLCBsZGFfYXVjLCAiLlxuIikKfQpgYGAKCiogU3VtbWFyaXplIFJ1bm5pbmcgVGltZQoKUHJlZGljdGlvbiBwZXJmb3JtYW5jZSBtYXR0ZXJzLCBzbyBkb2VzIHRoZSBydW5uaW5nIHRpbWVzIGZvciBjb25zdHJ1Y3RpbmcgZmVhdHVyZXMgYW5kIGZvciB0cmFpbmluZyB0aGUgbW9kZWwsIGVzcGVjaWFsbHkgd2hlbiB0aGUgY29tcHV0YXRpb24gcmVzb3VyY2UgaXMgbGltaXRlZC4gCgpgYGB7cn0KaWYocnVuLnBjYV9sZGEpewogIHRtX3RyYWluIDwtIHRyYWluLm1vZGVsLmVuZCAtIHRyYWluLm1vZGVsLnN0YXJ0CiAgdG1fdGVzdCA8LSB0ZXN0Lm1vZGVsLmVuZCAtIHRlc3QubW9kZWwuc3RhcnQKICBjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdGluZyB0cmFpbmluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3RyYWluWzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdGVzdGluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3Rlc3RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIG1vZGVsPSIsIHRtX3RyYWluWzFdLCAicyBcbiIpIAogIGNhdCgiVGltZSBmb3IgdGVzdGluZyBtb2RlbD0iLCB0bV90ZXN0WzFdLCAicyBcbiIpCn0KYGBgCgoKIyMjIFJlZmVyZW5jZQotIER1LCBTLiwgVGFvLCBZLiwgJiBNYXJ0aW5leiwgQS4gTS4gKDIwMTQpLiBDb21wb3VuZCBmYWNpYWwgZXhwcmVzc2lvbnMgb2YgZW1vdGlvbi4gUHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMsIDExMSgxNSksIEUxNDU0LUUxNDYyLg==